C/C++程序找错

6 篇文章 0 订阅
2 篇文章 0 订阅

C/C++程序找错

1.下面的代码输出是什么,为什么?

void foo(void)
{
    unsigned int a = 6; 
    int b = -20;
    (a + b > 6) ? puts("> 6") : puts("<= 6");
}
这无符号整型问题的答案是输出是">6"。原因是当表达式中存在有符号类型和无符号类型时所有的操作数都自动转换为无符号类型。因此-20变成了一个非常大的正整数,所以该表达式计算出的结果大于6。这一点对于应当频繁用到无符号数据类型的嵌入式系统来说是丰常重要的。

2.对下列代码进行评价

unsigned int zero = 0; /*1's complement of zero */
unsigned int compzero = 0xFFFF; 

对于一个int型不是16位的处理器为说,上面的代码是不正确的。应编写如下: 
unsigned int compzero = ~0; 

为什么啊?
unsigned int compzero = 0xFFFF; 
只写了2个字节,16位的才符合 
32位的可以写: 
unsigned int compzero = 0xFFFFFFFF; 
但unsigned int compzero = ~0;更安全,不管有多少位,直接取反,把所有的0都变成1

3.设有以下说明和定义:

typedef union 
{
    long i; 
    int k[5];
    char c;
} uni;
struct data
{
    int cat; 
    uni cow;
    double dog;
}DATE;
uni max;

则语句 printf("%d",sizeof(struct date)+sizeof(max));的执行结果是?
sizeof(max) = 20
sizeof(struct date) = 8 + 8 + 8

4.说出程序最后输出什么

#include<stdio.h> 
int inc(int a)
{
    return(++a);
}
int multi(int*a, int*b, int*c)
{
    return(*c = *a**b);
}

typedef int(FUNC1)(int in);
typedef int(FUNC2)(int*, int*, int*);

void show(FUNC2 fun, int arg1, int*arg2)
{
    int temp = arg1; 
    fun(&temp, &arg1, arg2); 
    printf("%d\n", *arg2);
}
main()
{
    int a;
    show(multi, 10, &a);
    return 0;
}
//程序最终输出100

5.使出毕生所学吐槽一下这段代码 (功能是字符串颠倒)

#include"string.h" 
int main()
{
    char*src = "hello,world"; 
    char* dest = NULL;
    int len = strlen(src); 
    dest = (char*)malloc(len);  //要为\0分配内存空间
    char* d = dest;
    char* s = src[len];         //&src[len - 1]指向最后一个字符 
    while(len--!=0)
        d++ = s--;              //*d++ = *s--
    printf("%s",dest);          //*d = 0;尾部要加上\0
    return 0;                   //尾部要加\0最后要内存释放
}

6.请问下面程序会出现什么情况?

#define Max_CB 500
void LmiQueryCSmd()
{
    unsigned char ucCmdNum;
    ......
    for(ucCmdNum=0;ucCmdNum<Max_CB;ucCmdNum++)
    {
        ......;
    }
}
//无限循环,因为Max_CB的值是500,而ucCmdNum最大的值是255(0~255),当ucCmdNum超过255就会溢出,变成0,所以ucCmdNum永远都不会大于Max_CB

7.以下 3 个有什么区别

char * const p; //常量指针,p 的值不可以修改
char const * p;//指向常量的指针,指向的常量值不可以改
const char * p;//和 char const *p一样

7.写出结果

char str1[] = "abc"; 
char str2[] = "abc";
const char str3[] = "abc";
const char str4[] = "abc"; 
const char *str5 = "abc"; 
const char *str6 = "abc"; 
char *str7 = "abc";
char *str8 = "abc";
cout << (str1 == str2) << endl; //false
cout << (str3 == str4) << endl; //false
cout << (str5 == str6) << endl; //true
cout << (str7 == str8) << endl; //true
//str1,str2,str3,str4  是数组变量,它们有各自的内存空间。 
//str5,str6,str7,str8  是指针,它们指向相同的常量区域。
//结果是:0 0 1 1

8.写出输出结果

main()
{
    int a[5]={1,2,3,4,5};
    int *ptr=(int *)(&a+1); 
    printf("%d,%d",*(a+1),*(ptr-1));
}
/*
*(a+1)就是 a[1],*(ptr-1)就是 a[4],执行结果是 2,5
&a+1 不是首地址+1,系统会认为加一个 a 数组的偏移,是偏移了一 个数组的大小(本 例是 5 个 int)
int *ptr=(int *)(&a+1);则 ptr 实际是&(a[5]),也就是 a+5
原因如下:
    &a 是数组指针,其类型为int(*)[5];而指针加1要根据指针类型加上一定的值,不同类型的指针+1之后增加的大小不同。a 是长度为 5 的 int 数组指针,所以要加 5*sizeof(int)。所以ptr实际是a[5]。但是 prt 与(&a+1)类型是不一样的(这点很重要)。所以prt-1只会减去sizeof(int*)。a,&a的地址是一样的,但意思不一样,a  是数组首地址,也就是 a[0] 的地址,&a 是对象(数组)首地址,a+1是数组下一元素的地址,即 a[1],&a+1 是下一个对象的地址,即 a[5]。
*/

9.以下表达式

int a=248; b=4;
int const c=21; 
const int *d=&a; 
int *const e=&b;
const int const *f =&a;

*c=32;  //err
d=&b;   //ok
*d=43;  //err
*e=34;   //ok
e=&a;   //err
f=0x321f;   //err

10.交换两个变量,不要第三个变量tmp

a ^= b;-----+
b ^= a;     +------->a ^= b ^= a;
a ^= b;-----+
(只能对 int,char,实型变量(double,float,long double)不参与位运算)
=======================
a = a + b;
b = a - b;
a = a - b;
=======================

11.(void *)ptr 和 (*(void**))ptr 的结果是否相同?

  • ptr 为同一个指针。

12.输出多少?并分析过程

unsigned short A = 10;
printf("~A = %u\n", ~A); //输出的是 uint
// A:0000 0000 0000 1010
//~A:‭1111 1111 1111 1111 1111 1111 1111 0101‬--->~A =0xfffffff5
//4294967285

char c=128; 
printf("c=%d\n",c);
//-128

13.分析下面的程序:

void GetMemory(char **p, int num)
{
    *p = (char *)malloc(num);
}
int main()
{
    char *str = NULL; 
    GetMemory(&str, 100); 
    strcpy(str, "hello"); 
    free(str); 
    if (str != NULL)
    {
        strcpy(str, "world");
    }
    printf("\n str is %s", str);
    getchar();
}
/*
答:输出 str is world。free只是释放的str指向的内存空间,它本身的值还是存在的。所以free之后,有一个好的习惯就是将str=NULL.此时str指向空间的内存已被回收,如果输出语句之前还存在分配空间的操作的话,这段存储空间是可能被重新分配给其他变量的,尽管这段程序确实是存在大大的问题,但是通常会打印出world来。这是因为进程中的内存管理一般不是由操作系统完成的,而是由库函数自己完成的。当你malloc一块内存的时候,管理库向操作系统申请一块空间(可能会比你申请的大一些),然后在这块空间中记录一些管理信息(一般是在你申请的内存前面一点),并将可用内存的地址返回。但是释放内存的时候,管理库通常都不会将内存还给操作系统,因此你是可以继续访问这块地址的。
*/

14.char a[10],strlen(a)为什么等于 15?运行的结果

#include "stdio.h"
#include "string.h" 
void main()
{
    char aa[10]; 
    printf("%d",strlen(aa));
}
//sizeof()和初不初始化,没有关系
//strlen()和初始化有关

15.数组指针和指针数组的区别

  • char (*str)[20];
    • str 是一个数组指针,即指向数组的指针
  • char *str[20];
    • str 是一个指针数组,其元素为指针型数据

16.求结构体大小

struct A
{
    char t:4;------------->4char k:4;------------->4unsigned short i:8;--->8unsigned long m;------>4个字节
};
1 + 1 + 4 = 6个字节,偏移 2 字节保证 4 字节对齐
sizeof(A) = 8

17.求结果:long a=0x801010;a+5=?

  • 0x801010 用二进制表示为:“1000 0000 0001 0000 0001 0000”,十进制的值为 8392720,再加上 5 就是 8392725

18.逆天题目

#include<iostream.h>
#include <string.h>
#include <malloc.h>
#include <stdio.h>
#include <stdlib.h>
#include <memory.h> 
typedef struct AA
{
    int b1:5; 
    int b2:2;
}AA;
void main()
{
    AA aa;
    char cc[100]; 
    strcpy(cc,"0123456789abcdefghijklmnopqrstuvwxyz"); memcpy(&aa,cc,sizeof(AA));
    cout << aa.b1 <<endl; 
    cout << aa.b2 <<endl; 
}
aa中的四个字节所存放的是0123的ASCII码,即00110000,00110001,00110010,00110011
int b1:5 = 10000----->11111--->-16
int b2:2 = 01
int 是有正负之分的

19.求函数返回值,输入 x=9999; 求结果?

int func (int x)
{
    int countx = 0; 
    while (x)
    {
        countx++; 
        x = x & (x - 1);
    }
    return countx;
}
//该函数是计算 9999 的二进制数值中有多少个 1 的函数
99999×102451225615
9×1024 中含有 1 的个数为 2512 中含有 1 的个数为 1256 中含有 1 的个数为 115  中含有 1  的个数为 4;
故共有 1 的个数为 8,结果为 81000 - 1 = 0111,正好是原数取反。这就是原理。用这种方法来求1的个数是很效率很高的。不必去一个一个地移位。循环次数最少。

20.分析:输出为什么是 4,1,-1,-4 ,ffffffff

struct bit
{
    int a : 3;
    int b : 2;
    int c : 3;
};
int main()
{
    bit s;
    char *c = (char*)&s;
    cout << sizeof(bit) << endl;//4
    *c = 0x99;//10011001
    cout << s.a << endl << s.b << endl << s.c << endl;
    //s.a = 100
    //s.b = 11
    //s.c = 001
    int a = -1;
    printf("%x", a);//ff ff ff ff
    return 0;
}
答:因为 0x99 在内存中表示为 100 11 001 , a = 001, b = 11, c = 100。当 c 为有符号数时, c = 100,    最高 1 为表示 c 为负数,负数在计算机用 补码表示,所以 c = -4;同理 b = -1;当 c 为有符合数时, c = 100,即 c = 4,同理 b = 3

21.分析以下代码

struct s1
{
    int i : 8;
    int j : 4;
    int a : 3; 
    double b;
};
struct s2
{
    int i : 8;
    int j : 4; 
    double b; 
    int a : 3;
};
printf("sizeof(s1)= %d\n", sizeof(s1));
printf("sizeof(s2)= %d\n", sizeof(s2)); 
result: 16, 24
答:第一个 struct s1 理论上是这样的,首先是 i 在相对 0 的位置,占 8  位一个字节,然后,j 就在相对一个字节的位置,由于一个位置的字节数是 4 位的倍数, 因此不用对齐, 就放在那里了,然后是 a,要在 3 位的倍数关系的位置上,因此要移一位,在 15 位的位置上放下,目前总共是 18 位,折算过来是 2 字节 2 位的样子,由于 double8 字节的,因此要在相对 0 要是 8 个字节的位置上放下,因此从 18 位开始到 8 个字节之间 的位置被忽略,直接放在 8 字节的位置了,因此,总共是 16 字节。(个人觉得这个解释有误) 

第二个最后会对照是不是结构体内最大数据 的倍数,不是的话,会补成是最大数据的倍数。

1. 一个位域必须存储在同一个字节中,不能跨两个字节。如一个字节所剩空间不够存放另一位域时,应从下一单元起存放该位域。也可以有意使某位域从下一单元开始。例如:   
struct bs  
{  
unsigned a:4  
unsigned :0 /*空域*/  
unsigned b:4 /*从下一单元开始存放*/  
unsigned c:4  
}  
在这个位域定义中,a占第一字节的4位,后4位填0表示不使用,b从第二字节开始,占用4位,c占用4位。
2. 由于位域不允许跨两个字节,因此位域的长度不能大于一个字节的长度,也就是说不能超过8位二进位
3. 位域可以无位域名,这时它只用来作填充或调整位置。无名的位域是不能使用的。例如: 
struct k  
{  
int a:1  
int :2 /*该2位不能使用*/  
int b:3  
int c:2  
};  
从以上分析可以看出,位域在本质上就是一种结构类型, 不过其成员是按二进位分配的

点击查看此题的经典解释

22.在对齐为4的情况下 分析下面程序的结果

struct BBB  
{  
    long num;  
    char *name;  
    short int data;  
    char ha;  
    short ba[5];  
}*p;  
p=0x1000000;  
p+0x200=____;  
(Ulong)p+0x200=____;  
(char*)p+0x200=____;  

剖析:
假设在32位CPU上,
sizeof(long) = 4 bytes
sizeof(char *) = 4 bytes
sizeof(short int) = sizeof(short) = 2 bytes
sizeof(char) = 1 bytes

由于是4字节对齐,
sizeof(struct BBB) = sizeof(*p)
= 4 + 4 + 2 + 1 + 1/*补齐*/ + 2*5 + 2/*补齐*/ = 24 bytes (经Dev-C++验证)

p=0x1000000;
p+0x200=____;
= 0x1000000 + 0x200*24   指针加法,加出来的是指针类型的字节长度的整倍数。就是p偏移sizeof(p) *0x200.

(Ulong)p+0x200=____;
= 0x1000000 + 0x200   经过ulong后,这已经不再是指针加法,而变成一个数值加法了

(char*)p+0x200=____;
= 0x1000000 + 0x200*1  结果类型是char*,这儿的1char的数据类型是1字节
  • 0
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值