C语言和数据结构与算法(99)

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类型

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

萌新小电阻

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值