每日一题(四)
9.11 结构体元素偏移
//int 占 2 个字节,char 占 1 个字节,float 占 4 个字节
struct stu{
union{
char bj[5];
int bn[2];
}class;//5
char xm[8];//8
float cj;//4
}xc;
问题:求sizeof(xc)?
我的答案:32
正确答案:20
分析:
无论结构体还是联合体,都讲究一个字节对齐,按照其内部最大元素内存大小对齐!!!
所以union中最大元素类型是int,这里是2字节,所以union按照2字节对齐,char bj[5]占了5字节,按照2字节对齐的时候再补一个字节,所以union的大小是6Byte;在struct中,class已经占了前6字节,char xm[8]中每一个char占1字节,所以char数组可以紧接着存放8个字节,现在内存大小为6+8=14字节,由于float需要4字节对齐,所以float从第16字节开始存储,占用4字节。至此struct内存大小占用20字节!
注意:结构体涉及数组的时候,我觉得可以把数组拆分开看作一个个元素,就好多了,只出现数组是不可以按照数组整体来内存对齐的.
结构体定义如下:
//int 占 2 个字节,char 占 1 个字节,float 占 4 个字节
struct stu{
union{
char bj[5];
int bn[2];
}class;//5
char xm[8];//8
float cj;//4
}xc;
问题:若xc地址为0X1000,求&xm[0]的值?
我的答案:0X1006
根据上面的内容,union大小为6Byte,所以相对于结构体的偏移地址是6,则紧挨着的xm[0]的地址就是0X1000+6=0X1006
为了提高 CPU 的存储速度,编译器会对一些变量的起始地址做了“对齐”处理。
对齐方式(变量存放的起始地址相对于结构的起始地址的偏移量)
char 偏移量必须为 sizeof(char) 即 1 的倍数
int 偏移量必须为 sizeof(int) 即 4 的倍数 (跟编译器有关,有可能是 2)
float 偏移量必须为 sizeof(float) 即 4 的倍数
double 偏移量必须为 sizeof(double) 即 8 的倍数
short 偏移量必须为 sizeof(short) 即 2 的倍数
9.12 指针、数组的声明
题目:
用变量a给出下面的定义
1、一个整型数(An integer)
2、一个指向整型数的指针( A pointer to an integer)
3、一个指向指针的的指针,它指向的指针是指向一个整数( A pointer to a pointer to an intege)
4、一个有10个整型数的数组( An array of 10 integers)
5、一个有10个指针的数组,该指针是指向一个整型数的。(An array of 10 pointers to integers)
6、 一个指向有10个整型数数组的指针( A pointer to an array of 10 integers)
7、 一个指向函数的指针,该函数有一个整型参数并返回一个整型数(A pointer to a function that takes an integer as an argument and returns an integer)
8、一个有10个指针的数组,该指针指向一个函数,该函数有一个整型参数并返回一个整型数( An array of ten pointers to functions that take an integer argument and return an integer
答案:
1:int a
2:int *a
3:int **a
4:int a[10]
5:int* a[10]
6:int (*a)[10]
7:int (*a)(int)
8:int (*a[10])(int)
[]
的优先级比*
要高,所以可以根据题中的要求来判断[]
和*
的先后执行顺序。
比如5题:一个10个指针的数组,指针指向整型数;首先是一个10个元素的数组,所以首先a[10],然后指定数组元素是指针指向整型,所以声明元素为int *
。
再比如6题:一个指针,指向有10个整型数的数组;首先是一个指针,所以先声明指针,即*a
,然后指针的指向是一个10个元素的整型数组,所以就成了int (*a)[10]
。
当涉及到函数的时候,分为三部分:函数返回值类型、指向函数的类型、函数的参数类型。指向函数的格式就是加上一个括号,代表函数体。
所以7题分析为:返回值和参数都是整型数,所以int () (int)
确定了两部分,然后是指向函数的指针,所以就有了int (*a)(int)
同理8题:返回值和参数都是整型,指向函数的是一个10个元素数组,数组的元素指向函数,所以就有int (*a[10])(int)
9.13 负数的求余运算
运行下列语句,求出x的值:
int x=10;
x += 3+x%(-3);//x=x+3+x%(-3)=13+
答案:14
分析:
x += 3+x%(-3)
中,+=
运算比较好判断,主要是x%(-3)
的判断不知道是1还是-1,在求余运算中,余数的符号与被除数的符号相同,所以在这里x=10,则得数就是1,即x += 3+1
易得x的值为14.
9.14 阅读代码
阅读下列代码,分析该代码的功能:
char *func(char *dest, const char *src, int count)
{
char *tmp = dest;
while (count) {
if ((*tmp = *src) != 0)
src++;
tmp++;
count--;
}
return dest;
}
分析:
先看参数和返回值,参数中第二个参数是const修饰的(指向的地址不可改变),所以第二个参数位置的字符串肯定是不能改变的,然后返回值也是一个char类型的指针,初步推断可能是一个字符串操作类的函数。
然后往函数内部分析,先创建了一个字符指针,指向传进来的dest
在while中根据count的大小进行遍历
if ((*tmp = *src) != 0)
中,先是将*tep = *src*
然后判断*tmp是否为空,相当于字符拷贝,然后自增、coun自减
可以看出此函数就是拷贝字符串的函数。
实际上strncpy的源码如下:
/*
* strncpy - Copy a length-limited, %NUL-terminated string
* @dest: Where to copy the string to
* @src: Where to copy the string from
* @count: The maximum number of bytes to copy
*
* The result is not %NUL-terminated if the source exceeds
* @count bytes.
*
* In the case where the length of @src is less than that of
* count, the remainder of @dest will be padded with %NUL.
*/
char *strncpy(char *dest, const char *src, size_t count) //从 src 复制count个字符 到 dest
{
char *tmp = dest;
while (count) {
if ((*tmp = *src) != 0) //把 src 的值 复制到 dest ,如果 src 的值 非空,那么指针自加。
//否则 指针不加,src 后续都是指向 0 ,即字符串结束,后续的 dest值都为 0
src++;
tmp++; // dest 的指针 自加
count--; //最大计数值 减 1
}
return dest; c
}
9.15 逗号表达式细节
求如下程序的输出:
int a[3][2] = {(0,1),(2,3),(4,5)};
int *p = a[0];
printf("%d",p[0]);
答案:1
分析:
这里分析一下程序,p指针指向a[0],也就是指向二维数组的首地址,所以p[0]的值就是二维数组的第一个值。
再看一下二维数组的定义:int a[3][2]={(0,1),(2,3),(4,5)}
,这里要注意,这里的初始化不是分段赋值,正常的分段赋值应该是这样:int a[3][2]={{0,1},{2,3},{4,5}}
,这里是逗号分隔的,所以是逗号表达式,实际的是连续赋值:int a[3][2]={1,3,5}
,所以p[0]=1.