1. 成员和数组元素有什么区别?
解析:
结构中的成员不一定是同一种类型,而数组中的元素都为同一种类型。
成员是通过成员名进行访问,数组元素是通过下标进行访问。
2. 结构名和数组名有什么不同?
解析:
结构名是一个标量。与其他任何变量一样,当结构名在表达式中作为右值使用时,它表示存储在结构中的值;作为左值使用时,它表示结构存储的内存位置。但是,当数组名在表达式中作为右值使用时,它的值是一个指向数组第1个元素的指针。由于它的值是一个指针常量,因为数组名不能作为左值使用。
3. 结构声明的语法有几个可选部分。请列出所有合法的结构声明形式,并解释每一个是如何实现的。
/*
** method: a label of struct, but no variable of struct.
*/
struct struct_a{
int a;
};
/*
** method2: a label of struct, a variable of struct.
*/
struct struct_b{
int a;
} b;
/*
** method3: no label of struct, a variable of struct.
*/
struct {
int a;
} c;
/*
** method4: no label of struct, no variable of struct, but a typedef, struct_d the type of struct.
*/
typedef struct {
int a;
} struct_d;
4. 下面的程序有没有错误?如果有,错误在哪里?
struct abc {
int a;
int b;
int c;
};
...
abc.a = 25;
abc.b = 15;
abc.c = -1;
解析:
有错误。abc是一个结构标签,而不是结构变量。只有结构变量才会被分配内存,结构标签不会分配内存。因此是错误的。
#include <stdio.h>
#include <stdlib.h>
struct abc {
int a;
int b;
int c;
};
int main( void ){
struct abc var;
/*
** can't pass compilation.
** abc.a = 25;
** abc.b = 15;
** abc.c = -1;
*/
var.a = 25;
var.b = 15;
var.c = -1;
printf( "var.a = %d, var.b = %d, var.c = %d\n", var.a, var.b, var.c );
return EXIT_SUCCESS;
}
/* 输出:
*/
5. 下面的程序有没有错误?如果有,错误在哪里?
typedef struct{
int a;
int b;
int c;
} abc;
...
abc.a = 25;
abc.b = 15;
abc.c = -1;
解析:
有错误。abc是一个结构类型,而不是结构变量。只有结构变量才会被分配内存,结构类型不会被分配内存。因此是错误的。
#include <stdio.h>
#include <stdlib.h>
typedef struct{
int a;
int b;
int c;
} abc;
int main( void ){
abc var;
/*
** can't pass compilation.
** abc.a = 25;
** abc.b = 15;
** abc.c = -1;
*/
var.a = 25;
var.b = 15;
var.c = -1;
printf( "var.a = %d, var.b = %d, var.c = %d\n", var.a, var.b, var.c );
return EXIT_SUCCESS;
}
/* 输出:
*/
6. 完成下面声明中对x的初始化,使成员a为3,b为字符串"hello",c为0。可以假设x存储在静态内存中。
struct {
int a;
char b[10];
float c;
} x = ;
#include <stdio.h>
#include <stdlib.h>
struct {
int a;
char b[10];
float c;
} x = { 3, "hello", 0 };
int main( void ){
printf( "x.a = %d, x.b = %s, x.c = %f\n", x.a, x.b, x.c );
return EXIT_SUCCESS;
}
/* 输出:
*/
7. 考虑下面这些声明和数据。
struct NODE{
int a;
struct NODE *b;
struct NODE *c;
};
struct NODE nodes[5] = {
{
5, nodes + 3, NULL
},
{
15, nodes + 4, node3 + 3
},
{
22, NULL, nodes + 4
},
{
12, nodes + 1, nodes
},
{
18, nodes + 2, nodes + 1
}
};
/* (Others declarations...) */
struct NODE *np = nodes + 2;
struct NODE **npp = &nodes[1].b;
对下面的表达式求值,并写出它的值。同时,写明任何表达式求值过程中可能出现的副作用,应该用最初显示的值对每个表达式求值(也就是说,不要使用某个表达式的结果来对下一个表达式求值)。假定nodes数组在内存中的起始位置为200,并且在这台机器上整数和指针的长度都是4字节。
表达式 值 表达式 值
nodes 200 &nodes[3].c->a 200
nodes.a 非法 &nodes->a 200
nodes[3].a 12 np 224
nodes[3].c 200 np->a 22
nodes[3].c->a 5 np->c->c->a 15
*nodes {5, nodes + 3, NULL} npp 216
*nodes.a 非法 npp->a 非法
(*nodes).a 5 *npp 248
nodes->a 5 **npp {18, nodes+2, nodes+1}
nodes[3].b->b 248 *npp->a 非法
*nodes[3].b->b {18, nodes+2, nodes+1}(*npp)->a 18
&nodes 200 &np 未知
&nodes[3].a 236 &np->a 224
&nodes[3].c 244 &np->c->c->a 212
/*
** In my computer, the size of pointer is 8 bytes, the size of int is 4 bytes.
** You can identify the correctness of question by using analogy.
*/
#include <stdio.h>
#include <stdlib.h>
struct NODE{
int a;
struct NODE *b;
struct NODE *c;
};
struct NODE nodes[5] = {
{
5, nodes + 3, NULL
},
{
15, nodes + 4, nodes + 3
},
{
22, NULL, nodes + 4
},
{
12, nodes + 1, nodes
},
{
18, nodes + 2, nodes + 1
}
};
int main( void ){
int i;
for( int i = 0; i < 5; ++i ){
printf( "nodes[%d].a = %d, nodes[%d].b = %p, nodes[%d].c = %p\n",
i, nodes[i].a, i, nodes[i].b, i, nodes[i].c );
printf( "&nodes[%d] = %p, &nodes[%d].a = %p, &nodes[%d].b = %p, &nodes[%d].c = %p\n",
i, &nodes[i], i, &nodes[i].a, i, &nodes[i].b, i, &nodes[i].c );
}
struct NODE *np = nodes + 2;
struct NODE **npp = &nodes[1].b;
printf( "sizeof(int) = %zd, sizeof(int *) = %zd\n", sizeof(int), sizeof(int *) );
printf( "sizeof(struct NODE) = %zd, sizeof(struct NODE *) = %zd\n", sizeof(struct NODE), sizeof(struct NODE *) );
int pointer_size = sizeof(int *);
printf( "nodes = %p\n", nodes );
/*
** can't pass compilation.
** because nodes is a pointer.
** nodes.a
*/
printf( "nodes[3].a = %d\n", nodes[3].a );
printf( "nodes[3].c = %p\n", nodes[3].c );
printf( "nodes[3].c->a = %d\n", nodes[3].c->a );
printf( "(*nodes).a = %d, (*nodes).b = %p, (*nodes).c = %p\n", (*nodes).a, (*nodes).b, (*nodes).c );
/*
** can't pass compilation.
** because nodes is a pointer, the priority of . operator is higher than * operator.
** *nodes.a;
*/
printf( "(*nodes).a = %d\n", (*nodes).a );
printf( "nodes->a = %d\n", nodes->a );
printf( "nodes[3].b->b = %p\n", nodes[3].b->b );
printf( "&nodes = %p\n", &nodes );
printf( "&nodes[3].a = %d\n", &nodes[3].a );
printf( "&nodes[3].c = %p\n", &nodes[3].c );
printf( "&nodes[3].c->a = %d\n", &nodes[3].c->a );
printf( "&nodes->a = %d\n", &nodes->a );
printf( "np = %p\n", np );
printf( "np->a = %d\n", np->a );
printf( "np->c->c->a = %d\n", np->c->c->a );
printf( "npp = %p\n", npp );
/*
** can't pass compilation.
** because npp is a pointer points to a pointer points to variable of struct NODE.
** *npp = nodes[1].b is a pointer points to variable of struct Node, but this pointer
** don't have the member of a, so it is illegal.
** npp->a;
*/
printf( "*npp = %p\n", *npp );
printf( "(**npp).a = %d, (**npp).b = %p, (**npp).c = %p\n", (**npp).a, (**npp).b, (**npp).c );
/*
** can't pass compilation.
** because npp is a pointer points to a pointer points to variable of struct NODE.
** *npp = nodes[1].b is a pointer points to variable of struct Node, but this pointer
** don't have the member of a, so it is illegal.
** the priority of -> operator is higher than * operator.
** *npp->a;
*/
printf( "(*npp)->a = %d\n", (*npp)->a );
printf( "&np = %p\n", &np );
printf( "&np->a = %p\n", &np->a );
printf( "&np->c->c->a = %p\n", &np->c->c->a );
return EXIT_SUCCESS;
}
/* 输出:
nodes[0].a = 5, nodes[0].b = 0000000000404088, nodes[0].c = 0000000000000000
&nodes[0] = 0000000000404040, &nodes[0].a = 0000000000404040, &nodes[0].b = 0000000000404048, &nodes[0].c = 0000000000404050
nodes[1].a = 15, nodes[1].b = 00000000004040A0, nodes[1].c = 0000000000404088
&nodes[1] = 0000000000404058, &nodes[1].a = 0000000000404058, &nodes[1].b = 0000000000404060, &nodes[1].c = 0000000000404068
nodes[2].a = 22, nodes[2].b = 0000000000000000, nodes[2].c = 00000000004040A0
&nodes[2] = 0000000000404070, &nodes[2].a = 0000000000404070, &nodes[2].b = 0000000000404078, &nodes[2].c = 0000000000404080
nodes[3].a = 12, nodes[3].b = 0000000000404058, nodes[3].c = 0000000000404040
&nodes[3] = 0000000000404088, &nodes[3].a = 0000000000404088, &nodes[3].b = 0000000000404090, &nodes[3].c = 0000000000404098
nodes[4].a = 18, nodes[4].b = 0000000000404070, nodes[4].c = 0000000000404058
&nodes[4] = 00000000004040A0, &nodes[4].a = 00000000004040A0, &nodes[4].b = 00000000004040A8, &nodes[4].c = 00000000004040B0
sizeof(int) = 4, sizeof(int *) = 8
sizeof(struct NODE) = 24, sizeof(struct NODE *) = 8
nodes = 0000000000404040
nodes[3].a = 12
nodes[3].c = 0000000000404040
nodes[3].c->a = 5
(*nodes).a = 5, (*nodes).b = 0000000000404088, (*nodes).c = 0000000000000000
(*nodes).a = 5
nodes->a = 5
nodes[3].b->b = 00000000004040A0
&nodes = 0000000000404040
&nodes[3].a = 4210824
&nodes[3].c = 0000000000404098
&nodes[3].c->a = 4210752
&nodes->a = 4210752
np = 0000000000404070
np->a = 22
np->c->c->a = 15
npp = 0000000000404060
*npp = 00000000004040A0
(**npp).a = 18, (**npp).b = 0000000000404070, (**npp).c = 0000000000404058
(*npp)->a = 18
&np = 000000000062FE00
&np->a = 0000000000404070
&np->c->c->a = 0000000000404058
--------------------------------
Process exited after 0.09453 seconds with return value 0
请按任意键继续. . .
*/
8. 在一台16位的机器上,下面这个结构由于边界对齐浪费了多少空间?在一台32位的机器上又是如何?
struct {
char a;
int b;
char c;
};
/*
** In my computer, int has the memory of 4 bytes.
*/
#include <stdio.h>
#include <stdlib.h>
int main( void ){
struct{
char a;
int b;
char c;
} var, var2;
int struct_size;
int char_size;
int int_size;
struct_size = sizeof(var);
char_size = sizeof(char);
int_size = sizeof(int);
printf( "sizeof(var) = %zd, sizeof(char) = %zd, sizeof(int) = %zd\n", struct_size, char_size, int_size );
printf( "wasteful time is %d bytes\n", struct_size - char_size * 2 - int_size );
return EXIT_SUCCESS;
}
/* 输出:
*/
9. 至少说出位段不可移植的两个理由。
解析:
1.int位段被当做有符号数还是无符号数。
2.位段中位的最大数目。许多编译器把位段成员的长度限制在一个整型值的长度之内,所以一个能够运行于32位整数的机器上的位段声明可能在16位整数的机器上无法运行。
3.位段中的成员在内存中是从左往右分配的还是从右往左分配的。
4.当一个声明指定了两个位段,且第2个位段比较大,无法容纳于第1个位段剩余的位时,编译器有可能把第2个位段放在内存的下一个字,也可能直接放在第1个位段后面,从而在两个内存位置的边界上形成重叠。
10. 编写一个声明,允许根据下面的格式方便地访问一个浮点值的单独部分。
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
struct FLOAT{
signed int symbol: 1;
signed int exponent: 7;
signed int fraction: 24;
};
int main( void ){
struct FLOAT var;
int counter;
int temp;
var.symbol = 1;
var.exponent = 2;
var.fraction = 24;
counter = 1;
temp = var.fraction;
while( temp / 10 != 0 ){
counter++;
temp /= 10;
}
printf( "var.symbol = %d, var_exponent = %d, var.fraction = %f, var = %f\n",
var.symbol, var.exponent, var.fraction / pow( 10, counter ),
var.fraction / pow( 10, counter ) * var.symbol * pow( 10, var.exponent ) );
return EXIT_SUCCESS;
}
/* 输出:
*/
11. 如果不使用位段,怎样实现下面这段代码的功能?假定使用的是一台16位的机器,它从左向右为位段分配内存。
struct {
int a: 4;
int b: 8;
int c: 3;
int d: 1;
} x;
...
x.a = aaa;
x.b = bbb;
x.c = ccc;
x.d = ddd;
解析:
x应该被声明为整型(或无符号整型),然后使用移位和屏蔽存储适当的值。单独翻译每条语句,生成了下面的代码:
x &= 0x0fff;
x |= (aaa & 0xf) << 12;
x &= 0xf00f;
x |= (bbb & 0xff) << 4;
x &= 0xfff1;
x |= (ccc & 0x7) << 1;
x &= 0xfffe;
x |= ( ddd & 0x1 );
如果只关心最终结果,下面的代码效率更高:
x = (aaa & 0xf) << 12 | \
(bbb & 0xff) << 4 | \
(ccc & 0x7) << 1 | \
(ddd & 0x1);
下面是另外一种方法:
/* note that the value of value continue to change, so it can appear x << 3 statement. */
x = aaa & 0xf;
x <<= 8;
x |= bbb & 0xff;
x <<= 3;
x |= ccc & 0x7;
x <<= 1;
x |= ddd & 1;
/*
12. 下面这个代码段将打印出什么?
struct {
int a: 2;
} x;
...
x.a = 1;
x.a += 1;
printf( "%d\n", x.a );
*/
#include <stdio.h>
#include <stdlib.h>
struct {
int a: 2;
} x;
struct {
unsigned a: 2;
} y;
/*... */
int main( void ){
x.a = 1;
printf( "after x.a = 1, x.a = %d\n", x.a );
x.a += 1;
printf( "after x.a += 1, x.a = %d\n", x.a );
x.a = -1;
printf( "after x.a = -1, x.a = %d\n", x.a );
x.a += 1;
printf( "after x.a += 1, x.a = %d\n", x.a );
x.a = 3;
printf( "after x.a = 3, x.a = %d\n", x.a );
y.a = 1;
printf( "after y.a = 1, y.a = %d\n", y.a );
y.a += 1;
printf( "after y.a += 1, y.a = %d\n", y.a );
y.a = 3;
printf( "after y.a = 3, y.a = %d\n", y.a );
y.a += 1;
printf( "after y.a += 1, y.a = %d\n", y.a );
return EXIT_SUCCESS;
}
/* 输出:
*/
13. 下面的代码段有没有错误?如果有,错误在哪里?
union{
int a;
float b;
char c;
} x;
...
x.a = 25;
x.b = 3.14;
x.c = 'x';
printf( "%d %g %c\n", x.a, x.b, x.c );
解析:
有错误。因为联合里面的变量共用一个存储空间。之后赋值的成员会覆盖之前成员变量的值。
#include <stdio.h>
#include <stdlib.h>
union{
int a;
float b;
char c;
} x;
int main( void ){
/*...*/
x.a = 25;
printf( "x.a = %d\n", x.a );
x.b = 3.14;
printf( "x.b = %f\n", x.b );
x.c = 'x';
printf( "x.c = %c\n", x.c );
printf( "%d %g %c\n", x.a, x.b, x.c );
return EXIT_SUCCESS;
}
/* 输出:
*/
14. 假定有一些信息已经赋值给一个联合变量,该如何正确地提取这个信息呢?
解析:
即在联合中的另一个成员变量赋值之前使用这个值,就能正确地提取这个信息。 参考13题。
15. 下面的结构可以被一个BASIC解释器使用,用于记住变量的类型和值。
struct VARIABLE{
enum { INT, FLOAT, STRING } type;
union{
int i;
float f;
char *s;
} value;
};
如果结构改写成下面这种形式,会有什么不同呢?
struct VARIABLE{
enum { INT, FLOAT, STRING } type;
union{
int i;
float f;
char s[MAX_STRING_LENGTH];
} value;
};
解析:
类型的大小将与MAX_STRING_LENGTH有关。这里需要注意边界对齐影响类型大小的问题。
#include <stdio.h>
#include <stdlib.h>
#define MAX_STRING_LENGTH 20
struct VARIABLE{
enum { INT, FLOAT, STRING } type;
union{
int i;
float f;
char *s;
} value;
};
struct VARIABLE2{
enum { INT, FLOAT, STRING } type;
union{
int i;
float f;
char s[MAX_STRING_LENGTH];
} value;
};
int main( void ){
struct VARIABLE var;
struct VARIABLE2 var2;
int var_size, var_size_2;
var_size = sizeof(var);
var_size_2 = sizeof(var2);
printf( "var_size = %zd, var_size_2 = %zd\n", var_size, var_size_2 );
return EXIT_SUCCESS;
}
/* 输出:
*/