第九章 字符串 问题: 回答:
1.优点:操作字符数组的效率和访问的灵活性较高。缺点会引发错误:溢出数组;使用的下标超出了字符串的边界;无法改变任何用于保存字符串的数组的长度。 2.它更合适,因为字符串的长度不能为负。此外,使用无符号值允许表示更长的字符串长度(在有符号的数量中为负)。它不太合适,因为涉及无符号表达式的算术运算可能会产生意想不到的结果。能够报告较长的字符串长度的“优势”很少是有价值的:在具有16位整数的机器上,只有长度超过32,767个字符的字符串才需要报告长度。在具有32位整数的机器上,只有长度超过2,147,483,647字节的字符串才需要它(这确实很少见)。 3.是的,这样可以更有效地进行后续的连接,因为查找字符串末尾的工作不需要重复。 4.使用库函数memcpy(y,x,50) 5.只有当数组中的最后一个字符已经为null时。字符串必须以NUL字节结束,strncpy不能保证会发生这种情况。但是,该语句不允许strncpy更改数组中的最后一个位置,因此如果该位置包含NUL字节(通过赋值或默认初始化静态变量),则结果将是一个字符串。 6.首先,无论使用的字符集是什么,前者都可以工作。后者将与ASCII字符集一起工作,但与EBCDIC字符集一起将失败。第二,前者将正常工作,无论地区是否已经改变;后者可能不会 7. register int ch;
...
for( pstring = message; ( ch = *pstring ) != ’\0’; ){
*pstring++ = toupper( ch );
}
8.如果缓冲区包含了一个字符串,memchr将内存中buffer的起始位置开始查找第一个包含0字节并返回一个指向该字节的指针。将这个指针减去buffer,可获得存储在这个缓冲区中字符串的长度。strlen函数完成相同的任务,不过strlen的返回值是个无符号类型的值,而指针减去的值应该是有符号的。但是缓冲区的数据并不是以NULL字节结尾,memchr函数返回一个NULL指针,将这个值减去buffer将产生一个无意义的结果。另一方面,strlen函数在数组的后面继续查找,直到最终发现NUL字节。 尽管使用strlen函数可以获得相同的结果,但一般而言使用字符串函数不可能查到NUL字节,因为这个值用于终止字符串,如果它是你需要查找的字节,则应该使用内存操纵函数。 第十章 结构与联合 结构基础知识:
数组元素可以通过下标访问,这只是因为数组元素长度相同。但是,在结构体情况并非如此,由于一个结构的成员可能长度不同,因此不能使用下标访问它们。相反,每个结构成员都有自己的名字,它们是通过名字访问的。 声明: struct tag{member-list}variable-list;
结构体的访问:一般用 . 访问,指针执行间接访问时用-> struct tag tag1;
tag.成员名
结构体的自引用 struct SELF_REF{
int a;
struct SELF_REF *b;
int c;
}
不完整声明
结构的初始化:如果初始化的值不够,剩余的结构成员将使用缺省值进行初始化。 结构体变量是一个标量,可以用于其他标量可以使用的任何场景。因此结构体可以用于函数。如果直接将结构体传给函数,比如:struct A{int a;int b;};void test(A a);你传一个A的结构体,这样的虽然可以产生正确结果但是效率很低。因为参数传值调用方法要求把参数的副本传递给函数,所以结构体会占据较大的空间,使用后,再丢弃。但是如果使用指向结构的指针,表现就不一样了,指针比整个结构要小很多,所以把它压在堆栈上效率高了很多。 #include<stdio.h>
struct Student {
int ID;
char name[8];
int age;
};
//结构体传递
void print_info(Student stu) {
printf("学生ID:%d\n",stu.ID);
printf("学生姓名:%s\n",stu.name);
printf("学生年龄:%d\n",stu.age);
}
//指针传递
void print_info2(Student const *stu) {
printf("学生ID:%d\n", stu->ID);
printf("学生姓名:%s\n", stu->name);
printf("学生年龄:%d\n", stu->age);
}
int main() {
Student stu = { 1,"xiao",20 };
print_info(stu);
print_info2(&stu);
return 0;
}
注意:向函数传递指针的缺陷在于函数现在可以调用程序的结构体变量进行修改,如果不希望如此,可以在函数中使用const防止这类修改。 位段:有些数据在存储时并不需要占用一个完整的字节,只需要占用一个或几个二进制位即可。例如开关只有通电和断电两种状态,用 0 和 1 表示足以,也就是用一个二进位。正是基于这种考虑,C语言又提供了一种叫做位段的数据结构 在结构体定义时,我们可以指定某个成员变量所占用的二进制位数(Bit),这就是位段。请看下面的例子:
struct bs{
unsigned m;
unsigned n: 4;
unsigned char ch: 6;
};
‘:’
后面的数字用来限定成员变量占用的位数。成员 m 没有限制,根据数据类型即可推算出它占用 4 个字节(Byte)的内存。成员 n、ch 被:
后面的数字限制,不能再根据数据类型计算长度,它们分别占用 4、6 位(Bit)的内存。注意:注重可移植性的程序应该避免使用位段,一个能够运行再32位整数的机器上的位段声明可能在16位整数的机器上无法运行。位段的唯一优点就是简化了源代码。 联合: 1)联合体是一个结构; 2)它的所有成员相对于基地址的偏移量都为0;3)此结构空间要大到足够容纳最"宽"的成员;4)其对齐方式要适合其中所有的成员;
由于联合体中的所有成员是共享一段内存的,因此每个成员的存放首地址相对于于联合体变量的基地址的偏移量为0,即所有成员的首地址都是一样的。为了使得所有成员能够共享一段内存,因此该空间必须足够容纳这些成员中最宽的成员。对于这句“对齐方式要适合其中所有的成员”是指其必须符合所有成员的自身对齐方式。 1:结构成员可以是所有不同的类型;它们通过名称访问;未使用的内存可以在相邻成员之间,以强制边界对齐要求。数组元素必须都是相同的类型;它们通过下标访问;并且在边界对齐的元素之间不会丢失任何空间。 2:结构是标量。与其他任何标量一样,当结构名在表达式中作为右值使用时,他表示存储在结构中的值;作为左值使用时,它表示结构存储的内存位置,但是,当数组名在表达式中作为右值使用时,它的值是一个指向数组第一个元素的指针,由于它的值是一个常量指针,因此指针不能作为左值使用。 8:对于16位整数,每个字符后面有一个字节被浪费。对于32位整数,有6个被浪费。请注意,为了保证结构在最严格的边界结束,c之后的空间会丢失。如果不这样做,下一个分配的变量可能不会从适当的边界开始。 注意题目是16位和32位,16位平台 char 1个字节8位 short 2个字节16位 int 2个字节16位 long 4个字节32位 指针 2个字节16位 (2)32位平台 char 1个字节8位 short 2个字节16位 int 4个字节32位 long 4个字节32位 long long 8个字节64位 指针 4个字节32位 9:a.字段是从右到左分配还是从左到右分配; b.字段是否太大,不能容纳一个单词的剩余比特,无论如何从那里开始,跨越边界到下一个单词或从下一个单词开始;C:对于声明为signed的字段,是否使用signed或unsigned算术;和.单个字段的最大大小。 14:用于存储数据的成员也必须用于读取数据。