2025_2_2 C语言中字符串库函数,结构体,结构体内存对齐
字符串相关库函数大多数都是需要字符串数组作为参数,因为它是可变的,但是有些参数也是不可变的const,即字符串常量。所以声明定义的时候需要区分清楚
1.strcpy
int main() {
char p[15];
strcpy(p, "Hello world");
return 0;
}
strcpy()函数就是字符串复制函数,第一个参数是需要复制的内存空间,第二个参数是需要被复制的字符串值。
缺点:不能判断数组是否越界
2.strncpy
strncpy 用于将源字符串的前 n 个字符复制到目标字符串 中。如果 src 的长度小于 n,则会用空字符(\0)填充 dest,直到复制的字符总数达到 n
int main() {
char str[10];
int size = sizeof(str) / sizeof(str[0]);
strncpy(str, "Hello world", size - 1);
str[size - 1] = '\0';
return 0;
}
- 当需要复制的字符串长度大于给定数组空间时,只复制第三个参数给定的长度,但是最后没有空间加上\0了,所以传第三个参数时需要留一个位置存放\0。
- 当需要复制的字符串长度小于给定数组空间时,strcpy后面剩余的所有位置都会被设置为\0.
3.strlen
strlen函数用于计算字符串的长度。不计算\0的长度。
4.strcat
strcat函数时字符串拼接函数
int main() {
char str[20] = "Hello ";
strcat(str, "world");
return 0;
}
strcat函数第一个参数是需要改变的被拼接字符数组,第二个参数是需要拼接上去的字符串值
缺点:不能判断字符数组是否越界
5.strncat
strcat函数时字符串拼接函数,但是有了越界检测的机制。
第三个参数是需要改变的字符数组还剩下多少空间
strncat 会在追加完成后自动在目标字符串的末尾添加空字符(\0),因此目标字符串始终是有效的以空字符结尾的字符串。所以设置长度时一般留一个位置存储\0
int main() {
char c[20] = "Hello ";
int sz = sizeof(c) / sizeof(c[0]);
strncat(c, "world", sz - strlen(c) - 1);
return 0;
}
6.strcmp
strcmp 按字典顺序比较两个字符串 s1 和 s2。比较是基于字符的 ASCII 值进行的,从第一个字符开始逐个比较,直到遇到不同的字符或字符串结束符(\0)为止。
int main() {
printf("%d\n", strcmp("abc", "abd"));
printf("%d\n", strcmp("abc", "abc"));
printf("%d\n", strcmp("abc", "ABC"));
return 0;
}
因为这个函数不会改变参数的值,即传入的都是const的字符指针
所以可以使用字符常量进行比较
7.结构体
结构体和数组都是存储数据的类型,数组存储相同数据类型的数据,结构体可以存储不同数据类型的数据。
struct student_s {
char name[10];
char gender;//f:female m:male
};
8.结构体的内存对齐规则
结构体的内存对齐规则非常重要,可以提高访问内存的效率
- 看数据类型占几个字节,内存以0开始计算
- 如果当前位置不是数据类型所占字节数的倍数,就需要填充字节数,以实现该变量的存储位置序号是它所占字节数的整数倍
- 总共的字节数是最大字节数的整数倍
typedef struct student_s {
int num;
char name[10];
char gender;//f:female m:male
}stu;
int main() {
stu stu1 ;
printf("%zd", sizeof(stu1));
return 0;
}
首先,num占4个字节,序号是0—3;name[10]占10个字节,数据类型是char,首地址应该存放在1的倍数位置上,即随便放,序号是4—14;gender占一个字节数据类型是char,首地址应该存放在1的倍数位置上,即随便放;最后一共占用15个字节,其中最大数据类型为int,占4个字节,所以最后的字节数应该是4的倍数,取最小是16.
typedef struct Test {
int num;
char c;
int n[5];
double d;
}test;
int main() {
test te;
printf("%zd", sizeof(te));
return 0;
}
int num;
占用4个字节。
由于它是结构体的第一个成员,它可以从任何地址开始(通常是0)。
char c;
占用1个字节。
紧接着num之后。由于char通常不需要对齐,它可以直接跟在num后面。
填充字节
在char c和接下来的int n[5]之间,编译器可能会插入填充字节以确保n[0](即数组的第一个元素)的地址是4的倍数。因为c位于地址4(num之后),所以编译器可能会插入3个填充字节(地址5-7)。
int n[5];
占用20个字节(5个int,每个4字节)。
从一个4字节对齐的地址开始(在这里是地址8)。
double d;
占用8个字节。
需要8字节对齐(在64位系统上通常是这样,32位系统可能只需要4字节对齐,但这里我们假设是64位系统或32位系统使用了更严格的对齐规则)。
n[4](数组的最后一个元素)结束于地址27。为了确保double d从8字节对齐的地址开始,编译器可能会在n[4]和d之间插入填充字节。从地址28到下一个8字节对齐的地址是32(因为32是8的倍数),所以编译器会插入4个填充字节(地址28-31)。
这样,结构体的内存布局如下:
int num;:地址0-3
char c;:地址4
填充字节:地址5-7
int n[5];:地址8-27
填充字节:地址28-31
double d;:地址32-39
总共为40字节