2.数据类型(22道)
2.1 用变量a给出下面的定义
(1) 一个整型数:int a;
(2) 一个指向整型数的指针(一重指针):int *a;
(3) 一个指向指针的指针,它指向的指针是指向一个整型数(二重指针):int **a;
(4) 一个有10个整型数的数组:int a[10];
(5) 一个有10个指针的数组,该指针是指向一个整型数的(指针数组):int *a[10];
(6) 一个指向有10个整型数数组的指针(数组指针):int (*a)[10];
(7) 一个指向函数的指针,该函数有一个整型参数并返回一个整型数(函数指针):int (*a)(int);
(8) 一个有10个指针的数组,该指针是指向一个函数的指针,该函数有一个整型参数并返回一个整型数(函数指针数组):int (*a[10])(int);
2.2 以下代码输出什么结果,为什么?
void foo(void)
{
unsigned int a = 6; //无符号整型
int b = -20;//有符号整型
(a + b > 6) ? printf(">6\n") : printf("<=6\n");
}
答案:输出“>6”,因为a+b是一个无符号整型,结果为无符号整型,所以不可能小于0,所以输出“>6”。
解析:
这段代码考察的是C语言中的整型提升问题,当有符号整型和无符号整型进行运算时,有符号整型会被提升为无符号整型,然后再进行运算。
注意:正数的补码是其本身,负数的补码是其反码+1,所以-20的补码为0xffffffe4,无符号整型的最大值为0xffffffff,所以a+b的结果为0xffffffea,即4294967276,大于6,所以输出“>6”。
2.3 写出float和“零值”比较的语句。
if(x > -0.000001 && x < 0.000001)
{
printf("x is zero\n");
}
解析:
因为计算机在处理浮点数时,会有精度损失,所以不能直接用==来判断浮点数是否为0,而是要判断浮点数是否在一个范围内,且不能将浮点型用“==”或者“!=”比较,应该用“>”或者“<”比较,
例如上面的代码,就是判断x是否在-0.000001到0.000001之间,如果是,则认为x为0。
2.4 以下代码有什么错误?
#include<stdio.h>
void main()
{
char *s = "AAA";
s[0] = 'B';
printf("%s\n",s);
}
答案:“AAA”是一段字符串常量,s是指针,指向这个字符串常量,所以声明s的时候就有问题,应该是const char* s = "AAA";然后又因为是常量,所以对s[0]的赋值操作是不合法的。
2.5 下面代码输出的是什么?
#include<stdio.h>
void main()
{
int *a = (int *)2;
printf("%d\n",a + 3);
}
答案:输出14
解析:
a是一个指针,指向地址为2的地方,然后a+3,就是指向地址为2+3*4=14的地方,所以输出14。
2.6 下面代码运行后会是什么规律?
#include<stdio.h>
#define N 500
void main()
{
unsigned char count;///范围 unsigned char 0~255
for (count = 0; count < N ;count++)
{
printf("---%d---\n",count);
}
}
答案:进入不断打印count值的死循环。
解析:
因为unsigned char的范围是0~255,所以count++之后,count的值会一直在0~255之间循环,所以会进入死循环。
2.7 下面函数的返回值是?
int foo(void)
{
int i;
char c = 0x80;//char c 范围是-128~127,0x80是-128,所以c=-128
i = c;
if(i > 0)
return 1;
return 2;
}
答案:返回值为2
解析:
因为c是一个char型变量,范围是-128~127,0x80是-128,所以c=-128,然后i=c,所以i也是-128,然后i>0,所以返回值为2。
2.8 结构体内存对齐原则?
答案:
(1)第一个成员的首地址(地址偏移量)为0。
(2) 成员对齐:以4字节对齐为例,如果自身类型小于4字节,则成员的首地址是自身类型大小的整数倍;如果自身类型大于4字节,则成员的首地址是4的整数倍。
若有内嵌结构体,则内嵌结构体的首地址也要对齐,只不过自身类型大小用内嵌结构体的最大成员类型大小来表示,数组可以拆分看做n个数组元素,不用整体看作一个类型。
(3)最后结构体对齐:以4字节为列,如果结构体中最大的成员类型小于4字节,则大小补齐为结构体中最大成员类型的整数倍;如果结构体中最大的成员类型大于4字节,则大小补齐为4的整数倍,内嵌结构体也要补齐。
注意:32位编译器,一般对齐方式是4字节。
例子1:
#include <stdio.h>
struct MyStruct {
char a; // 1字节
int b; // 4字节,需要对齐到4的倍数
char c; // 1字节
short d; // 2字节,需要对齐到2的倍数
};
int main()
{
printf("sizeof(MyStruct)=%lu\n", sizeof(struct MyStruct));//sizeof(struct MyStruct)=12
return 0;
}
例子2内嵌结构体
struct InnerStruct {
int x; // 4字节
char y; // 1字节
};
struct OuterStruct {
char a; // 1字节
struct InnerStruct b; // 8字节,需要对齐到4的倍数
short c; // 2字节,需要对齐到2的倍数
};
int main()
{
printf("sizeof(InnerStruct)=%lu\n", sizeof(struct InnerStruct));//sizeof(InnerStruct)=8
printf("sizeof(OuterStruct)=%lu\n", sizeof(struct OuterStruct));//sizeof(OuterStruct)=16
return 0;
}
2.9 结构体内存对齐的原因?
答案:
(1)平台原因(移植原因):不是所有硬件平台都能访问任意地址上的任意数据。
(2) 性能问题:数据结构(尤其是栈) 应该尽可能的在自然边界上对齐,因为访问未对齐的内存,处理器需要做两次内存访问,而访问对齐的内存只需要一次。
2.10 给定的位域结构体,它在内存中占用多少字节?(32位编译器)?
struct A
{
char t : 4; //4位
char k : 5; //4位
unsigned short i : 8; //8位
unsigned long m; //4字节
}
答案:根据结构体内存对齐原则,共占用8字节。
解析:
因为位域是按照位来分配内存的,所以位域的大小不能超过一个字节,所以t和k都是4位,所以占用一个字节,i是8位,所以占用一个字节,m是4字节,所以共占用8字节。
2.11 在32位系统中,有如下结构体,那么sizeof(fun)的数组是?
#pragma pack(1) //指定按1字节对齐
struct fun
{
int i; //4字节
double d; //8字节
char c; //1字节
};
答案:13
解析:
因为#pragma pack(1)指定了按1字节对齐,所以结构体中的成员都是按照1字节对齐的,所以sizeof(fun) = 4 + 8 + 1 = 13。
2.12 数组元素首地址和数组地址的异同?
解析:
(1)异:数组元素首地址是数组元素的首地址,例如int a[10],a的值是数组元素首元素地址,所有a+1就是第二个元素的地址;数组地址是数组的首地址,
int类型占用4个字节,所以两者相差4。而&a是数组地址,所有&a+1就是向后移动(10*4)个单位,所以两者相差40。
(2)同:数组元素首地址和数组地址的值是相等的。
2.13 以下代码输出什么结果?
#include<stdio.h>
void main()
{
int a[5] = {1,2,3,4,5};
int *ptr = (int *)(&a + 1);
printf("%d,%d",*(a + 1),*(ptr - 1));
}
答案:2,5
解析:
首先,a是一个数组,a+1指向数组的下一个元素,即a[1]。因此,*(a+1)输出2。
接下来看ptr。&a表示数组a的地址,&a+1则表示数组a后面一个整型的地址。
因为a是5个整型的数组,因此&a+1指向的是a[5]的地址。将&a+1强制转换为指向整型的指针int*,可以得到ptr的值。
因为ptr指向a[5]的地址,因此*(ptr-1)指向a[4],输出5。
因此代码输出2,5。
2.14 判断下面表达式是否正确
char str[2][3] = {"a","b"}; //正确,str是一个可存放两个字符串的二维数组,每个字符串长度为3,所以str[0] = "a",str[1] = "b"
char srt[2][3] = { {1,2},{3,4},{5,6} }; //错误,行列不匹配。
char str[] = {"a","b"}; //错误,字符数组不能存放两个字符串。
char str[2] = {"a","b"}; //错误,字符数组不能存放两个字符串。
2.15 查看下面代码,p[6]等于多少?
int a[10] = {1,2,3,4,5,6,7,8,9,0};
int *p = &a[1];
答案:等于8
解析:
p是一个指针,指向a[1],即2,然后p+6,就是指向a[7],即7,所以p[6] = 8。
2.16 以下代码输出什么结果?
#include<stdio.h>
void main()
{
char *srt[] = {"ab","cd","ef","gh","ij","kl"};//指针数组
char *t;
t = (str + 4)[-1];
printf("%s\n",t);
}
答案:输出“gh”。
解析:
str是一个指针数组,每个元素都是一个指向字符的指针,所以str[0] = "ab",str[1] = "cd",str[2] = "ef",str[3] = "gh",str[4] = "ij",str[5] = "kl"。然后t = (str + 4)[-1],str+4指向str[4],即"ij",然后(str + 4)[-1],即str[3],所以t = "gh"。
2.17 变长数组是什么?
答案:变长数组是C99标准中的一个新特性,它允许在定义数组时使用变量作为其长度,例如:
#include<stdio.h>
void main()
{
int n;
sanf("%d",&n);
int a[n];
}
解析:
变长数组的长度是可以改变的,但是变长数组的长度必须是一个常量,例如上面的代码,n是一个变量,所以a[n]是一个变长数组,但是n是一个常量,所以a[n]是一个变长数组。
2.18 bool类型包含于哪个头文件?
答案:stdbool.h
注意:C89标准中没有bool类型,C99标准中才有bool类型,所以C89标准中没有stdbool.h头文件,C99标准中才有stdbool.h头文件。
C89需要自己定义。
#define TREU 1
#define FALSE 0
typedef int bool;
bool res = TRUE;
2.19 结构体struct和联合体union的区别?
(1)两者最大的区别在于内存的使用。
(2)结构体成员拥有自己的内存,各自使用互不干扰,遵循内存对齐原则。
(3)联合体所有成员共用一块内存,修改一个成员会影响其他成员,并且只有一位成员可以得到这块内存的使用权。一个联合体变量的总长度应至少能
容纳最大成员的变量,且需要进行内存对齐。
例子:
#include <stdio.h>
#include <string.h>
// 结构体
struct person {
char name[50];
int age;
float salary;
};
// 联合体
union data {
int num;
float score;
char grade;
};
int main() {
// 使用结构体
struct person p1;
strcpy(p1.name, "Tom");
p1.age = 30;
p1.salary = 5000.5;
printf("Name: %s\n", p1.name);
printf("Age: %d\n", p1.age);
printf("Salary: %.2f\n", p1.salary);
printf("\n");
// 使用联合体
union data d1;
d1.num = 10;
printf("Num: %d\n", d1.num);
d1.score = 90.5;
printf("Score: %.2f\n", d1.score);
d1.grade = 'A';
printf("Grade: %c\n", d1.grade);
return 0;
}
输出结果:
Name: Tom
Age: 30
Salary: 5000.50
Num: 10
Score: 90.50
Grade: A
解析:这个示例定义了一个结构体person,其中包含了姓名、年龄和薪水等属性。同时,还定义了一个联合体dta,其中包含了整数、浮点数和字符等属性。在mai()函数中,分别初始化了一个结构体对象和一个联合体对象,并输出了它们的属性值。需要注意的是,在联合体中,使用了不同类型的数据成员,但是在输出时只输出了最后一次赋值的数据成员的值。这是因为联合体所有的成员在内存中是共享同一块空间的,赋值一种类型后,其他类型的值就被覆盖了。
2.20 给了一个地址a,分别强转类型为:int变量,int指针,数组指针,指针数组,函数指针。
答案:
(1)int变量:int a
(2)int指针:(int *)a
(3)数组指针:(int (*)[10])a
(4)指针数组:(int *[10])a
(5)函数指针:(void (*)(int))a
2.21 执行完下面代码,c的值是多少?
unsigned int a = 1; //无符号整型
int b = 0;//有符号整型 在内存中补码依然是0
int c = 0;//有符号整型
c = a + b > 0 ? 1 : 2;
答案:c = 1
解析:
a是一个无符号整型,b是一个有符号整型,a+b是一个无符号整型,所以a+b>0为真,所以c = 1。
2.22 C语言中不同数据类型之间的赋值规则?
(1)整数与整数之间(char,short,int,long):
①长度相等:内存中的数据不变,只是按照不同的编码来解析。
②长赋值给短:截取低位,然后按短整型的数据类型解析。
③短赋值给长:如果都是无符号数,短整型高位补0;如果都是有符号数,短整型高位补符号,
如果一个有符号数,一个无符号数,那么先将短整数进行位数扩展,过程保持数据变,然后按照长整数的数据类型解析数据。
例子1:
char c = 'A';
int i = 65;
c = i; // i是int类型,可以将其自动转换为char类型
(2)整数与浮点之间
①整数转浮点数:小数部分为0,整数部分与整数相等。
②浮点数转整数:截取整数部分。
例子2:
int i = 3;
float f = 3.14;
i = f; // f是float类型,将会被截断成3
f = i; // i是整数类型,将会自动转换为3.0
(3)float与double之间
①float转double:不会丢失精度。
②double转float:会丢失精度。
注意:整数在内存中都是以补码的形式存储的。
例子3:
float f = 3.14;
double d = 3.14;
f = d; // d是double类型,可以将其自动转换为float类型
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)