常用函数
一、字符串函数
1、字符串在程序中如何保存使用?
char A[10] = “hello”; -> 将常量区"hello"拷贝到数组A内存空间。
char *p = “hello”; -> 将常量区"hello"首元素的地址保存在指针变量p中。
2、在linux下,有些关于字符串函数提供用户使用。
1)计算字符串实际长度 ->strlen()
2)比较两个字符串是否匹配 -> strcmp()
3)拷贝字符串到某段内存中 -> strcpy()
4)追加字符串 ->strcat()
1、计算字符串实际长度 strlen( )-> 不包含’\0’在内
Ubuntu:man 3 strlen
NAME -> 函数的功能
strlen - calculate the length of a string -> 校对字符串长度
#include <string.h> -> 头文件
size_t strlen(const char *s); -> 函数原型
s: 需要计算的字符串的首元素的地址
返回值:字符的个数。
例子:
#include <stdio.h>
#include <string.h>
int main()
{
char A[100] = "helloworld";
char *p = "helloworld";
printf("sizeof(A) = %d\n",sizeof(A));//100
printf("sizeof(p) = %d\n",sizeof(p));//4
printf("strlen(A) = %d\n",strlen(A));//10
printf("strlen(p) = %d\n",strlen(p));//10
return 0;
}
结论: sizeof()计算空间大小,strlen()计算字符串的长度。
2、比较两个字符串是否匹配 -> strcmp()
1、与整型变量是否相等比较
int a = 200;
int b = 100;
if(a == b) -> 整型数据使用"=="
来进行判断
2、比较两个字符串能不能用"=="?
char A[100] = “helloworld”;
char *p = “helloworld”;
if(A == p) -> 不可以,只能判断两个地址是否相等,这样对比是没有任何意义,只有对比内容才有意义。
3、 如何比较两个字符串? -> 只能使用strcmp() -> man 3 strcmp
NAME
strcmp, strncmp - compare two strings 比较两个字符串
#include <string.h>
int strcmp(const char *s1, const char *s2);
int strncmp(const char *s1, const char *s2, size_t n);
s1
:需要进行比较的字符串1的地址
s2
:需要进行比较的字符串2的地址
n
: 只匹配前n个字节
返回值:
s1
与s2
匹配 -> 0
s1
与s2
不匹配 -> 非0
4、例子:
#include <stdio.h>
#include <string.h>
int main()
{
int ret;
char A[100] = "helloworld";
char *p = "hello";
ret = strncmp(A,p);
if(ret == 0){
printf("match!\n");
}
else{
printf("no match!\n");
}
return 0;
}
练习:有一张银行卡,密码是gec123456,请求用户输入密码,如果密码正确,则输出密码的长度,如果密码错误,则重新输入!如果输入的次数超过5次,则程序直接退出。
#include <stdio.h
#include <string.h>
int main(int argc,char *argv[])
{
char passwd[20] = "gec123456"; //银行卡密码
char input_passwd[20] = {0}; //清空数组
char *p = input_passwd;
int count = 0;
while(count < 5)
{
scanf("%s",p);
if(strcmp(passwd,p) == 0)
{
printf("passwd length = %d\n",strlen(passwd));
break;
}
else{
count++;
continue;
}
}
return 0;
}
3、拷贝字符串到某段内存中 -> strcpy()
char A[10] = “hello”;
char A[10] = {“hello”};
char A[10] = {‘h’,‘e’,‘l’,‘l’,‘o’};
char A[10]; -> 如果定义没有初始化,则以后都不能整体初始化。
A[0] = ‘h’;
…
strcpy()
-> 如果定义没有初始化,则以后还能使用strcpy()整体初始化。 -> man 3 strcpy
char A[10];
A = “hello” //错误
strcpy(A,“hello”); //正确
NAME
strcpy, strncpy - copy a string
SYNOPSIS
#include <string.h>
char *strcpy(char *dest, const char *src);
char *strncpy(char *dest, const char *src, size_t n);
dest:需要把字符串拷贝到的空间的起始地址,这个空间必须足够大。
src:需要拷贝的字符串的地址。
n: 只拷贝src前n个字节
返回值:
成功: 返回指向dest内存空间的地址
失败: NULL
#include <stdio.h>
#include <string.h>
int main()
{
char A[11] = "helloworld";
char *p = "abc";
int i;
for(i=0;i<11;i++)
{
printf("A[%d] = %c\n",i,A[i]);
}
strcpy(A,p);
for(i=0;i<11;i++)
{
printf("A[%d] = %c\n",i,A[i]);
}
printf("A = %s\n",A);
return 0;
}
4、字符串函数 -> 追加字符串 strcat()
字符串函数 -> 追加字符串strcat()
-> man 3 strcat
使用格式:
#include <string.h>
char *strcat(char *dest, const char *src);
char *strncat(char *dest, const char *src, size_t n);
src
:需要追加到另一个字符串默认的字符串的地址。 -> 拷贝之后会覆盖dest的\0。
dest
:被追加的字符串的空间,空间必须足够大。
返回值:指向dest区域的首元素的地址。
验证: overwriting the terminating null byte (’\0’) at the end of dest, and then adds a terminating null byte.
src覆盖dest的\0,并且会在拼接之后在字符串的默认添加一个\0。
#include <stdio.h>
#include <string.h>
int main()
{
/* 可以 */
char A[20] = "hello";
strcat(A,"world");
/* 可以 */
char A[20] = "hello";
char *p = A;
strcat(p,"world");
/* 不可以 */
char *p = "hello";
strcat(p,"world");
printf("p = %s\n",p); //helloworld
return 0;
}
总结:学习过4个字符串函数的使用场景
- strlen() -> 确定某些数据的字节数 -> 文件IO/系统编程/网络编程 write()/send()
- strcmp() -> 检索特征数据 -> 链表/网络编程 -> 对比特征值/协议是否一致
- strcpy() -> 用于初始化字符数组
char A[10]="helloworld"; 对
char A[10];
A = "helloworld"; 不对
char A[10];
strcpy(A,"hello");
- strcat -> 拼接字符串,仅限追加功能 -> 后期使用sprintf() 替代 strcat()
5、 数组清零方式
1、定义数组的同时初始化0
例子: char str[50] = {0};
特点:只能初始化(清零)一次。
2、清空某段内存空间。 -> bzero() -> man 3 bzero
bzero - write zero-valued bytes -> 函数的功能
#include <strings.h> -> 头文件
void bzero(void *s, size_t n); -> 函数原型
s:需要清零的内存的地址
n:需要清零的字节数
返回值:无
特点:多次调用,多次清零。
#include <stdio.h>
#include <strings.h>
int main()
{
char A[10];
int i;
for(i=0;i<10;i++)
{
printf("A[%d] = %c\n",i,A[i]);
}
bzero(A,sizeof(A));
for(i=0;i<10;i++)
{
printf("A[%d] = %c\n",i,A[i]);
}
return 0;
}
二、堆空间
1、堆空间的特点:主动申请,主动释放
2、如何申请堆空间? -> malloc() -> man 3 malloc
#include <stdlib.h>
void *malloc(size_t size);
size:需要申请的字节数
The memory is not initialized. -> 内存没有被初始化过。
返回值:
成功: 指向堆空间的起始地址
失败: NULL
例子:
void *pa = malloc(4) -> 在堆空间申请4个字节,然后在栈空间申请一个指针变量pa指向堆空间。
int *p = pa; -> 把堆空间的pa赋值赋值给指针变量p
int pa; -> 在栈空间申请4个字节
int *p = &pa; -> 把栈空间pa变量的地址赋值给指针变量p
例题1: 申请了堆空间,堆空间的值会是什么?
#include <stdio.h>
#include <stdlib.h>
int main()
{
int A[3];
printf("A = %p\n",A);
int *p = malloc(sizeof(int)*3); // int A[3];
printf("p = %p\n",p);
printf("p[0] = %d\n",p[0]); //0
printf("p[1] = %d\n",p[1]); //0
printf("p[2] = %d\n",p[2]); //0
free(p);
return 0;
}
3、如何释放空间? -> free() -> man 3 free
#include <stdlib.h>
void free(void *ptr);
ptr:需要释放堆空间的地址
返回值:无
一般做法:free掉指向堆区指针之后,会让指针指向NULL。
free§;
p = NULL;
三、堆空间与栈空间在内存中容易出错的点
下列代码是否正确?如果正确,请指出代码的含义,如果出错,则说明错误的原因。
1、栈区
char A[10] = “hello”; //把常量区的hello拷贝到变量A数组中
strcpy(A,“world”); //把常量区的world拷贝到变量A数组中
============================
char *p = “hello”; //把常量区的hello的首元素的地址赋值给变量p
strcpy(p,“hello”); //段错误,因为p只能存放地址,不能存放字符串。
============================
char A[10];
strcpy(A,“hello”); //可以
============================
char *p;
strcpy(p,“hello”); //段错误,因为p只能存放地址,不能存放字符串。
============================
char A[10]; //在栈区中申请10个字节,使用变量A间接访问这个数组
char *p = A; //在栈区申请4个字节,使用变量p间接访问这片内存,将数组的A的首元素的地址赋值给p
strcpy(p,“hello”); //将常量区的hello拷贝到p指向的栈区空间
============================
2、堆区
char *p = malloc(sizeof(char)*10); //在堆区中申请10个字节,使用p间接访问这片内存空间。
p = “hello”; //给p赋值了常量区hello的地址,就是说现在p不是指向堆,而是指向常量区。
============================
char *p = malloc(sizeof(char)*10); //在堆区中申请10个字节,使用p间接访问这片内存空间。
strcpy(p,“hello”); //可以,把常量区的hello拷贝到堆区!
四、结构体
1、什么是结构体?
将多个不同类型的变量加入到一个集合中,这个集合就称之为结构体
。
由于结构体中存在不同类型的变量,所以每一个变量都需要用户自己定义。
可以在结构体中定义:
基本数据类型: char short int long float double
非基本数据类型:int A[3]
、 int*p
、char B[2][3]
、char *px
、char pa[5]
、 int(*pfun)(int)
不可以在结构体中定义:函数
2、 如何定义结构体?
关键词:struct
模型:
struct 结构体名字{
/* 结构体的组成变量 */
例子:
int a;
char b;
int A[3];
int *p;
}; -> 后面记住有一个分号,不然报错。
其实结构体就是一种新的数据类型。
例子:
struct data{
int a;
char b;
};
在这里只是先说: “struct data” 这种新类型由 {int a;char b;}组成。
3、如何定义结构体的变量?
struct mydata{
char name[20];
int age;
};
struct mydata -> 新的数据类型
定义变量公式: 数据类型 + 变量名;
定义整型变量 int a;
定义结构体变量 struct mydata A;
4、结构体的指针如何定义?
定义结构体变量: struct mydata A;
1)先写一个*
2)在*后面写一个变量的名字 *p
3)确认指针指向的内容是什么。struct mydata A;
4)将指向的内容的变量名去掉struct mydata
5)将第4步的结果写在第2步的前面 struct mydata *p
;
结果:struct mydata *p;
变量名: p
数据类型: struct mydata *
5、结构体的变量与指针如何访问成员?
1)设计结构体模型
struct mydata{
char name[20];
int age;
};
2)定义一个结构体的变量
struct mydata gec;
3)结构体变量使用"."
来对成员进行访问。
公式: 结构体变量名.成员名字
例子:
strcpy(gec.name,"ggy");
gec.age = 10;
4)结构体指针使用"->"访问成员。
公式: 结构体指针变量名->成员名字
例子:
strcpy(p->name,"ggy");
p->age = 10;
6、结构体初始化值
1)先定义一个变量,后使用./->对成员进行赋值
struct mydata gec;
strcpy(gec.name,“ggy”);
gec.age = 10;
2)使用初始化列表
struct mydata gec = {“helloworld”,20}; -> 等价于将"helloworld"拷贝到name成员的空间中,20就赋值给age;
struct mydata gec = {“helloworld”}; -> 后面没有赋值的成员 变量为0 指针为NULL
3)使用另外一个结构体给某个结构体整体赋值
struct mydata gec;
struct mydata A = {“hello”,20};
gec = A;
练习: 做一个通讯录,里面存放三个同学信息,每一个同学包含: 姓名,年龄,电话号码 -> 结构体数组。
-
先分别对三个同学注册。
-
输出三个同学的全部信息。
#include <stdio.h>
struct mydata{
char name[20];
int age;
char tel[20];
};
int main()
{
struct mydata A[3];
int i; //0~2
for(i=0;i<3;i++)
{
printf("pls input %d name",i+1);
scanf("%s",A[i].name);
printf("pls input %d age",i+1);
scanf("%d",&A[i].age);
printf("pls input %d tel",i+1);
scanf("%s",A[i].tel);
}
printf("====================================================\n");
for(i=0;i<3;i++)
{
printf("%s %d %s\n",A[i].name,A[i].age,A[i].tel);
}
return 0;
}
7、计算结构体类型在内存中占用的字节数。
基本数据类型:char 、short、int 、long、 float、 double -> 占用字节数固定。
例子: int a -> 占用4个字节
自定义结构体数据类型占用多少个?
struct mydata{
char name[20];
int age;
char tel[20];
};
例子1:
struct mydata{
char name[20];
int age;
};
sizeof(struct mydata) = ? //24
例子2:
struct mydata{
int age;
char name[20];
};
sizeof(struct mydata) = ? //24
例子3:
struct mydata{
int age;
char name;
};
sizeof(struct mydata) = ? //8
例子4:
struct mydata{
int age;
char name;
short a;
};
sizeof(struct mydata) = ? //8
例子5:
struct mydata{
char a;
char b;
short c;
};
sizeof(struct mydata) = ? //4
=例子6:=
struct mydata{
char a;
short b;
char c;
};
sizeof(struct mydata) = ? //6
例子7:
struct mydata{
char a;
short b;
char c;
int d;
int e;
};
sizeof(struct mydata) = ? //16
例子
struct mydata{
char a;
int (*px)[3];
char A[10];
int b;
char **p;
short c;
char a;
short f;
int e;
char B[18];
}; //60
例子
struct mydata{
short a;
int (*px)(int,int);
char b;
char c;
int d;
char A[13];
short e;
int **p;
int f;
char *B[3];
}; //52
计算结构体占用空间大小的方法:
1)从上往下计算,而不是根据类型的大小来计算。
2)看看当前结构体中最大的成员占用是2/4 -> 2的话结果就是2的倍数 4的话结果就是4的倍数。
3)如果某个结构体成员计算完,但是当前没有对齐,那么剩余的字节大小就应该与下一个成员大小进行比较(字节对齐)
- 下一个成员的大小大于当前行剩余的字节大小
结果: 剩余的字节补0,下一个成员开辟新的一行空间。 - 下一个成员的大小小于/等于当前行剩余的字节大小
结果:将下一个成员塞进剩余的字节中
4)全部结构体成员处理完后,看看2的倍数/4的倍数来进行补0。
六、联合体
1、为什么会有联合体?
为了解决结构体在内存中占用比较大情况。
例子:
struct mydata{
char name[20];
int age;
char tel[20];
};
sizeof(struct mydata) = 44; -> 占用非常多空间 -> 如果定义为联合体,则大大节省。
2、联合体定义方式与结构体一致,只需要修改关键词即可。
struct -> union
例子:
union mydata{
char name[20];
int age;
char tel[20];
};
sizeof(union mydata) = 20
但是使用的时候,只能同时使用一个成员。
计算联合体的空间大小:
1)看看联合体哪个成员是占用空间最大的。
2)看看当前结构体中最大的成员占用是2/4 -> 2的话结果就是2的倍数 4的话结果就是4的倍数。
3)如果该成员占用字节数是2/4的倍数,那么联合体的大小就是该变量占用的字节数。
4)如果该成员占用字节数不是2/4的倍数,那么看情况来补0。
3、由于联合体只能同时使用一个成员,不能整体赋值。
例子:
union data{
char name[10];
int age;
};
union data A = {“ggy”,10};
编译警告; warning: excess elements in union initializer
==========================================================
#include <stdio.h>
#include <string.h>
#include <strings.h>
union data{
char name[10];
int age;
};
int main()
{
union data A;
strcpy(A.name,"ggy");
printf("A.name = %s\n",A.name);
bzero(A.name,sizeof(A.name));
A.age = 10;
printf("A.age = %d\n",A.age);
return 0;
}
=====================================================
4、在联合体中,所有成员的起始地址都是一致的。
#include <stdio.h>
#include <string.h>
#include <strings.h>
union data{
char name[10];
int age;
};
int main()
{
union data A;
printf("A.name:%p\n",A.name);
printf("A.age:%p\n",&A.age);
return 0;
}
执行:
A.name:0xbfb3b220
A.age:0xbfb3b220
5、联合体也是可以作为函数的参数
#include <stdio.h>
#include <string.h>
#include <strings.h>
union data{
char name[10];
int age;
};
void fun(union data B) //B = A
{
printf("B.name = %s\n",B.name);
return;
}
int main()
{
union data A;
strcpy(A.name,"ggy");
fun(A);
return;
}
七、枚举类型
1、什么是枚举类型?
枚举类型其实就是int类型常量,意义就是给常量一个身份。
例子: ok -> 1 warning -> 0 error -> -1
2、枚举类型一般作用地方?
1)可以作用于switch语句。
2)在函数返回值。 return 枚举类型数据类型
void fun()
{
if(xxxx)
return warning; -> 别人看起来就知道返回值是什么情况。
if(yyyy)
return ok;
if(zzzz)
return error;
}
3、如何定义枚举类型?
关键词: enum
模型:
enum mydata{
ok, //如果没有赋值,第一个成员默认从0开始! //0
warning, //后面没有赋值的成员默认在前面的基础+1 //1
error, //2
};
enum mydata{
ok = 1,
warning = 0,
error = -1,
};
enum mydata{
ok = 10,
warning = 20,
error, //21
};
八、typedef关键词
1、什么是typedef?作用是什么?
typedef
其实就是 type
+ define
,给一种数据类型
(基本数据类型/非基本数据类型)取一个新的别名
。
例子: 给int这种类型取一个新的名字叫aaa。 -> 很少给基本数据类型取别名 1%
给struct mydata这种数据类型取新的名字叫mydata。 -> 给非基本数据类型取别名 99%
作用: 简化复杂的数据类型。
2、如何使用typedef给数据类型取别名?
使用格式: typedef + 数据类型 + 新的名字
1)给int这种类型取一个新的名字叫aaa。
typedef int aaa; -> 原则来typedef后面的这个"int aaa"看看,类型是int,剩下的就是aaa就是它的新名字。
在后面的代码中,定义一个整型变量:aaa a;
例子:
#include <stdio.h>
int main()
{
typedef int aaa; //int 等价于 aaa
aaa a = 10;
printf("a = %d\n",a);
return 0;
}
2)给struct mydata这种数据类型取新的名字叫mydata。
typedef struct mydata{
char name[20];
int age;
}mydata; //struct mydata 等价于 mydata
mydata bbb; -> 定义一个结构体变量。