C经典题详解

1、以下为Linux下的32 位C程序,请计算sizeof 的值。
char  str[] = “Hello” ;                               
char   *p = str ;                                      
int    n = 10;                                                    
请计算
(1)sizeof (str ) =                  (2)sizeof ( p ) =(指针大小)               
(3)sizeof ( n ) =(int类型数据的大小)

【标准答案】

(1)6、(2)4 、(3 )4

解析:sizeof 获取数据在内存中所占用的存储空间,以字节为单位来计数。由于C语言会自动在双引号" "括起来的内容的末尾补上"\0"代表结束,sizeof会计算\0为一个字节。

2、计算大小

(1) void Func ( char str[100])                           
{                                          
…… ;                                                                                                   
请计算sizeof( str ) =  (数组在做为函数参数时均化为指针)

}                              
(2) void * p = malloc( 100 );                                                
请计算sizeof ( p ) = 

【标准答案】(1)4、(2)4

解析sizeof无法统计动态分配的内存大小,动态分配是运行过程中得到大小的,也就是说C++中new出来的内存,sizeof都无法统计的。

静态数组:是在声明时已经确定子数组大小的数组。int foo[10]; int arr[] = {1,2,3,4,5,6,7};

动态数组:指在声明时没有确定数组大小的数组。int *p= new int[10]; char *p

注意:由于64位系统的寻址能力比32位系统高一倍,所以指针的大小在32位系统中为 4字节,64位系统中为8字节。

3、用变量a 给出下面的定义
1) 一个有10个指针的数组,该指针是指向一个整型数的;
2)  一个指向有10个整型数数组的指针;
3) 一个指向函数的指针,该函数有一个整型参数并返回一个整型数;
4) 一个有10个指针的数组,该指针指向一个函数,该函数有一个整型参数并返回一个整型数;

【标准答案】

1)  int *a[10]
2)  int *p;
     int a[10];
     p=a;
3)  int *func(int)
4)  int (*a[10])(int)

4、设有以下说明和定义:
typedef union {long i; int k[5]; char c;} DATE;
struct data { int cat; DATE cow; double dog;} too;
DATE max;
则语句printf("%d",sizeof(struct date)+sizeof(max)); 的
执行结果是:_____

【标准答案】

(联合体[默认四字节对齐]的大小取决于他所有成员中占用空间最大的一个成员的大小

example:

union u               //u的大小是其中最大的double类型成员a,所以sizeof(u) = sizeof(double) = 8;
{
 double a;
 int b;
};

union u1           // u1的大小是char[13] 类型的数组,但由于另一个成员int b ,所以要以4对齐,13以4对齐就是补3位到16;
{
 char a[13];
 int b;
};

union u2          // u2的大小是char[13]类型的数组,不需要补齐,所以长度为13;
{
 char a[13];
 char b;
};

(结构体对齐[默认四字节对齐]

struct A{
    int a;
    char b;
    short c; 
};
struct B{
    char b;
    int a;
    short c;
};
 
int main (){
    cout<<sizeof(A)<<endl;
    cout<<sizeof(B)<<endl;  
 

输出结果为: 8  12

其内存分配如下:

在32位系统中,每次取地址都是4个字节4个字节的取。
在结构体A的定义中,int类型的a占4字节,char类型的b和short类型的c可以共用4个字节,在访问b和c时取的地址都一样,需要再分出b和c来。
在结构体B的定义中,char类型的b占1个字节,后面的int要占用4个字节,但是只剩3个,所以int类型的a只好再重新找一个地址。后面的c顺次存放。

定义B时,如果没有struct的对齐,而是依次存放的话,那么a会占用2 3 4 5这四个字节,如下图:(暂时不考虑c)

那么在读取a时,我们需要先取前4位地址(1--4),从中找出a的3位,再取4位地址(5--8),找出a的1位,再拼接成a,这需要两次读取,分离和一次拼接,无疑是麻烦的。

(对齐时候要考虑地址前后都是有数据的,所以要从整体来考虑)

可以用语句 #pragma pack (N)  来指定以N字节的方式对齐。

)

DATE是一个union, 变量公用空间.  里面最
大的变量类型是int[5],  占用20个字节.  所以它的大小是
20
data 是一个struct,  每个变量分开占用空间.  依次为int4 + 
DATE20 + double8 = 32.
所以结果是20 + 32 = 52.

5、请问以下代码有什么问题:
int main()
{
char a;
char *str=&a;
strcpy(str,"hello");
printf(str);
return 0;
}

【标准答案】

没有为str分配内存空间,将会发生异常问题出在将一个字符串复制进一个字符变量指针所指
地址,因为越界进行内在读写而导致程序崩溃。

正确的修改方法:

char a[10];
char *str=a;

6、请问以下代码有什么问题:
char* s="AAA";
printf("%s",s);
s[0]='B';
printf("%s",s);
有什么错?


【标准答案】"AAA" 是字符串常量,编译正确,运行出错,因为s[0]='B'试图修改字符串常量,字符串常量不能修改。

这里涉及到常量的问题,我们进一步做延伸:

常量存储总结
局部变量、静态局部变量、全局变量、全局静态变量、字符串常量以及动态申请的内存区

1、局部变量存储在栈中
2、全局变量、静态变量(全局和局部静态变量)和字符串常量存储在静态存储区
3、new和malloc申请是在内存的堆中
 

补充说明:
1、栈中的变量内存会随着定义所在区间的结束自动释放;而对于堆,需要手动free,否则它就一直存在,直到程序结束;
2、对于静态存储区,其中的变量常量在程序运行期间会一直存在,不会释放,且变量常量在其中只有一份拷贝,不会出现相同的变量和常量的不同拷贝。

7、int (*s[10])(int)  表示的是什么意思

【标准答案】

int (*s[10])(int)  函数指针数组,每个指针
指向一个int func(intp aram) 的函数。int后的()包括的是函数

8、c和c++ 中的struct有什么不同?


【标准答案】

c和c++ 中struct的主要区别是c中的struct
不可以含有成员函数,而c++ 中的struct可以。

c++ 中struct和class的主要区别在于默认的存取权限不同,
struct默认为public ,而class默认为private

9、以下函数执行会出现什么问题?

void getmemory(char *p)
{
        p=(char *) malloc(100);
        strcpy(p,“hello world”);
}
int main( )
{
        char *str=NULL;
        getmemory(str);
        printf(“%s/n”,str);
        free(str);
        return 0;
}

【标准答案】

定义的*p为局部变量,在函数getmemory外部释放会造成异常,且printf不会有任何输出。

编译器总是要为每个参数制作临时副本,getmemory函数,指针参数p的副本是_p,编译器使_p=p。如果函数体内的程序修改了_p的内容,就导致参数p的内容作相应的修改,这就是指针可以用作输出参数的原因。在本例中,_p申请了新的内存,只是把_p所指的内存地址改变了,但是p丝毫未变。所以GetMemory并不能输出任何东西。

可以这样修改:

void getmemory(char *p)
{
        strcpy(p,“hello world”); //内部修改了_p的值,也就修改了p的内容。
}
int main( )
{
        char *str = (char *) malloc(100); //在外边初始化字符串指针
        getmemory(str);
        printf(“%s/n”,str);
        free(str);
        return 0;
}

10、32位系统,写一段代码,先向内存地址0x67A9上整型变量的值写为0xAA66,然后将该变量的bit4和bit5位设置为1和0;

【标准答案】

int *p;   //先定义一个int类型的指针变量:p

p=(int *)0x67A9; //指针变量p,指向的是地址。(int *)强制转换成整型指针,如果这样看就容易理解了:(int *) p 将p强制转换为int类型的指针,此时p为地址,同理0x67A9也为地址。

*p=0xAA66; //指针变量p中存储的是0xAA66

*p=*p | 0x10;  //先设置bit4位

*p=*p & 0xFFDF; //再设置bit5位

11、char szstr[10];
        strcpy(szstr,"0123456789");
        产生什么结果?为什么?

【标准答案】

考点是:C 字符数组与字符串的使用及加结束符'\0'的问题

C语言规定了一个“字符串结束标志”,以字符'\0'代表。如果有一个字符串,其中第10个字符为'\0',则此字符串的有效字符为9个。也就是说,在遇到第一个字符'\0'时,表示字符串结束,由它前面的字符组成字符串。

所以sizeof不需要识别 “\0” 结束符,但是strlen需要识别到“\0”结束符,否则字符串长度识别出错。

sizeof将“\0”作为一个字符,而strlen不是。

strlen(...)是函数,要在运行时才能计算。 参数必须是字符型指针(char*), 且必须是以'\0'结尾的。

 sizeof(...)是 运算符,而不是一个函数,获得保证能容纳实现所建立的最大对象的字节大小;

案例分析:

1)        char str[]="12345";

或给字符串加上大括号:char str[]={"12345"};

这种方法定义时,系统会自动在字符串的末尾加上字符串结束符,即 ‘\0’,

此时sizeof(str)=6  strlen(str)=5

2)        char str[10]={'1','2','3','4','5'};

这种方法定义时,系统会自动从未初始化的元素开始,将之后的元素赋为\0,如上面的数组str中的元素实际上是:'1','2','3','4','5','\0','\0','\0','\0','\0'

此时sizeof(str)=10  strlen(str)=5

3)        char str[]={'1','2','3','4','5'};

这种方法定义时,系统不会自动在字符串的末尾加上字符串结束符strlen不知何时结束,所以无法判断其大小

此时用sizeof()函数可以正确求出其所占的内存大小;但用strlen()函数不能正确求出其长度,因为strlen是通过\0判断字符串结束的。

所以,采用该方法定义时,一般人为地加上\0,即char str[]={'1','2','3','4','5', '\0'};

有了上述的了解,我们看一下strcpy的作用:strcpy把含有'\0'结束符的字符串复制到另一个地址空间;

str只能装10个字符,0123456789也是10个字符,无法添加\0,所以运行必然会报错。

考点:1、strcpy的作用;2、字符串中\0规则;

12、void main()
{
char aa[10];
printf(“%d”,strlen(aa));
}                                          
会出现什么问题?打印结果是是多少?

【标准答案】

理解了11题,这道题解答起来就很容易了,

由于aa[10]没有初始化,所以strlen找不到'\0',打印结果值未知.

13、给定结构

struct A
{
char t;
char k;
unsigned short i;
unsigned long m;
}; 问sizeof(A) = ?

【标准答案】

理解了第4题,这道题很容易解答,大小为:8

14、struct name1{
char str;
short x;
int num;
} ;求sizeof(name1)?

【标准答案】

理解了第4题,这道题也很容易解答,大小也为:8

16、struct name2{
char str;
int num;
short x;
}; 求sizeof(name2)?

【标准答案】

理解了第4题,这道题也很容易解答,大小为:12,因为四字节对齐取。

17、程序哪里有错误
wap( int* p1,int* p2 )
{
int * p;  //(int)malloc(4); is ok
*p = *p1;
*p1 = *p2;
*p2 = *p;
}

【标准答案】

*p没有分配存储空间,为空指针。

当*p等于p1指向的值时,*p是没有分配存储空间的,用什么来存储p1的值呢?

18、(void *)ptr 和(*(void**))ptr 的结果是否相同?其中ptr为同一个指针。
【标准答案】

这种类型的题纯粹是变态的人来设计的,根本没有多大意义。

但是解题思路可以这样分析:

(void *)ptr:代表ptr是一个空类型指针,ptr表示的是地址;

(*(void**))ptr:拆解为两段第一段(void**)ptr,第二段(*(void**))ptr

第一段的中的ptr为“地址的地址”,第二段用*来指向“地址的地址”,即为地址;

所以(void *)ptr 和(*(void**))ptr 值相同。

19、要对绝对地址0x100000赋值,我们可以用
(unsigned int*)0x100000 = 1234;
那么要是想让程序跳转到绝对地址是0x100000去执行,应该怎么做?

【标准答案】

解题思路:执行函数的时候,即可以跳转到函数处。所以我们可以先定义一个函数指针,然后将函数指针指向的地址赋值给0x100000

void(*fun)(void);

fun=0x100000;

*fun; //调用该地址指向的内容

20、关于内存的思考题(1),你能看出有什么问题?

void GetMemory(char *p) 

        p = (char *)malloc(100); 

void Test(void) 

        char *str = NULL; 
        GetMemory(str); 
        strcpy(str, "hello world"); 
        printf(str); 

请问运行Test 函数会有什么样的结果? 

【标准答案】

运行报错,char型指针变量并没有被分配内存空间,所以使用strcpy向它赋值会报错这道题与第9题类似

21、关于内存的思考题(2)你能看出有什么问题?

char *GetMemory(void) 

        char p[] = "hello world";  
        return p; 

void Test(void) 

        char *str = NULL; 
        str = GetMemory(); 
        printf(str); 

 【标准答案】

运行不会报错,但是无法输出hello world,输出结果未知。

先看一下参考文档:通过汇编理解返回char p []和char *p 中P的区别_ivnetware的博客-CSDN博客

该题考察的知识点有两个:

1、char p[] 和 char *p在函数中的返回值

2、常量区的字符串是否可以修改 

考点1:

在函数中 char p[] = "hello world";  含义是:将常量区的字符串,拷贝到栈空间中,这个栈空间是新开辟的,由p[]开辟。p指向这个栈空间的首地址。

如果改为char *p = "hello world";  含义是:p指向的是常量区存储"hello world"的首地址。

由于 char p[]是在函数中定义的局部变量,所以函数执行完将被释放回收,这时再指向新开辟的栈空间首地址肯定不是"hello world"。

考点2:

char p[]="hello world" 存储常量区的字符串需要在栈上新开辟空间,所以可以更改"hello world"的值。但是char *p="hello world"中p指向的是常量区的数据,无法更改。

22、关于内存的思考题(3)你能看出有什么问题?

void GetMemory2(char **p, int num) 

        *p = (char *)malloc(num); 

        printf("*p=%p\n", *p);

void Test(void) 

        char *str = NULL; 
        GetMemory(&str, 100); 

        printf("str=%p\n", str); 
        strcpy(str, "hello"); 
        printf(str); 

请问运行Test 函数会有什么样的结果? 
 【标准答案】

运行没有问题,但是会有内存泄漏的情况,因为malloc申请位置在堆区,需要手动释放。

分析 char **p,把p看为“地址的地址“,即 &&x

*p则指向的是&x,即为地址,(char *)malloc(num)返回的也是地址

23、关于内存的思考题(4)你能看出有什么问题?

void Test(void) 

        char *str = (char *) malloc(100); 
        strcpy(str, “hello”); 
        free(str); 
        if(str != NULL) 
        { 
                strcpy(str, “world”); 
                printf(str); 
        } 

请问运行Test 函数会有什么样的结果? 

 【标准答案】

结果无法预测, free(str); 之后str为野指针。

参考21题的介绍,可以把char *str = (char *) malloc(100);改为:char str[100];

在堆区新申请空间,这样就可以修改str的内容了。

24、关键字volatile有什么含意? 并给出三个不同的例子。

 【标准答案】

1)volatile 保证了不同线程对共享变量操作的可见性。

2)volatile 禁止了指令的重排序

例子:

int a = 0;
// 线程 A
a = 1;           // 1
flag = true;     // 2

// 线程 B
if (flag) { // 3
  int i = a; // 4
}

单看上面的程序好像没有问题,最后 i 的值是 1。但是为了提高性能,编译器和处理器常常会在不改变数据依赖的情况下对指令做重排序。假设线程 A 在执行时被重排序成先执行代码 2,再执行代码 1;而线程 B 在线程 A 执行完代码 2 后,读取了 flag 变量。由于条件判断为真,线程 B 将读取变量 a。此时,变量 a 还根本没有被线程 A 写入,那么 i 最后的值是 0,导致执行结果不正确。那么如何程序执行结果正确呢?这里仍然可以使用 volatile 关键字。

这个例子中, 使用 volatile 不仅保证了变量的内存可见性,还禁止了指令的重排序,即保证了 volatile 修饰的变量编译后的顺序与程序的执行顺序一样。那么使用 volatile 修饰 flag 变量后,在线程 A 中,保证了代码 1 的执行顺序一定在代码 2 之前。

例子死记硬背不容易理解,在项目中遇到时候再做仔细分析。

25、嵌入式系统经常具有要求程序员去访问某特定的
内存位置的特点。在某工程中,要求设置一绝对地址
为0x67a9的整型变量的值为0xaa66。编译器是一个纯
粹的ANSI编译器。写代码去完成这一任务。

 【标准答案】

int *p;

p=(int *)0x67a9;

*p=0xaa66;

这道题和19题题干类似

26、头文件中的ifndef/define/endif 干什么用?

 【标准答案】

防止头文件被重复引用

27、#include  <filename.h> 和 #include  “filename.h” 有什么区别?

 【标准答案】

#include  <filename.h> 是首先从标准库的路径开始搜索

#include  “filename.h”是首先从当前工程目录下搜索

28、const   有什么用途?(请至少说明两种)

 【标准答案】

1)定义变量,防止变量值被改变

2)定义指针,防止指针指向的值被改变

3)定义类的成员函数时,表明这个成员函数不能改变类的变量

等等

29、static有什么用途?(请至少说明两种)

 【标准答案】

1)定义全局变量为静态全局变量,作用域为当前文件内

2)定义局部变量为静态局部变量,作用域为该函数内

3)定义函数为静态函数,则作用域在定义该函数的文件内

4)类中定义的静态函数,表示该函数只能访问类中的定义的静态变量,非静态变量不能访问。

30、堆栈溢出一般是由什么原因导致的?

 【标准答案】

1)动态申请的资源没有回收

2)数组访问越界

31、如何引用一个已经定义过的全局变量?

 【标准答案】

1)头文件引用

2)external该变量

32、全局变量可不可以定义在可被多个.C 文件包含的头文件中?为什么?

 【标准答案】

不能,编译器会报多重引用的错误

可以在多个c文件中定义相同的全局变量,前提是加static,让该全局变量变为静态全局变量,作用域只在该c文件中。

33、队列和栈有什么区别

 【标准答案】

队列:先进先出

栈:先进后出

34、Heap与stack的差别

 【标准答案】

heap是堆,如new和malloc申请的存储空间在堆上,堆空间需要手动释放。

stack是栈,局部变量存储在栈空间,栈空间能够自动释放。

35、用宏定义写出swap(x,y),即交换两数

 【标准答案】

#define swap(x,y) x=x+y;y=x-y;x=x-y;

36、已知一个数组table ,用一个宏定义,求出数据的
元素个数。

【标准答案】

需要参考11题

define LENGHT sizeof(table)/sizeof(table[0])

37、A.c 和B.c两个c文件中使用了两个相同名字的static变量,编译的时候会不会有问题?这两个static变量会保存到哪里(栈还是堆或者其他的地方?)

【标准答案】

不会报错同名错误,因为static定义的变量只在定义的文件中使用,static变量保存在静态存储区。

38、static局部变量和普通局部变量有什么区别

【标准答案】

static局部变量只被初始化一次,下一次依据上一次结果值;而普通局部变量在函数被调用时候都会初始化。

39、程序的局部变量存在于___ 中,全局变量存在于
____中,动态申请数据存在于___ 中。

【标准答案】

参考第6题,栈区,静态存储区,堆区。

40、什么是预编译,何时需要预编译?
【标准答案】

预编译又叫预处理, c提供的预处理功能主要有以下三种:

 1 )宏定义 

 2 )文件包含,如某个头文件被多个c文件包含

 3 )条件编译

何时需要预编译?

总是使用不经常改动的大型代码体

41、用两个栈实现一个队列的功能?要求给出算法和
思路!

【标准答案】

由33小题可知,栈是先进后出,队列是先进先出。

所以先定义两个栈A,B

入队:将数据push到A中

出队:将A中的数据push到B中,然后在将数据pop出即可。

51、对于一个频繁使用的短小函数,在C 语言中应用什
么实现,在C++ 中应用什么实现?

【标准答案】

C使用宏定义 #define

C++使用inline

52、用预处理指令#define  声明一个常数,用以表
明1年中有多少秒(忽略闰年问题)

#define SECPERYEAR (60*60*24*365)

53、在C++  程序中调用被C 编译器编译后的函数,
为什么要加extern “C”?

【标准答案】

最终目的是为了在C++中匹配C的函数名,因为C++支持函数的重载,而C不支持。如果C函数名在C++中编译后,在库中的名字将不同。

56、语句for(  ;1  ;) 有什么问题?它是什么意思?
【标准答案】

没有判断条件,和while(1)一样,为死循环。

57、do……while和while……do有什么区别

【标准答案】

第一个是循环了之后再判断

第二个是先判断再循环

58、下面代码将输出什么?

#include <stdio.h>
int main()
{
        int a,b,c,d;
        a=10;
        b=a++; //b=10, a=11
        c=++a; //c=12, a=12
        d=10*a++;//d=120, a=13
        printf("b,c ,d:%d,%d,%d",b,c,d );
        return 0;
}
【标准答案】

a++要在下一条语句中生效,直接赋值其实还没有生效。

++a则是直接生效

10,12,120

59、计算题

unsigned char *p1;
unsigned long *p2;
p1=(unsigned char *)0x801000;
p2=(unsigned long *)0x810000;
请问p1+5= ;
p2+5= ;
【标准答案】

易错点,需要将10进制转为16进制

0x801005、0x810014【32位机unsigned long为4个字节,64位机占8个字节】

60、计算题

main()
{
int a[5]={1,2,3,4,5};
int * ptr=(int*)(&a+1);
printf(“%d,%d”,*(a+1),*(ptr-1));
}

解析:

关键点在&a

a既是数据名,又是指向数组第一个元素的指针。sizeof(a)=20, 此时a的类型为int[5]数组。sizeof(*a)=4,因为有取值符*,表示把a当成一个指针(int*),而a指向数组的首地址。

(&a+1)先取变量a的地址,并根据a的地址获得下一个与a同类型的相邻地址。根据前面所说的a的类型为int[5]数组。&a+1=&a+sizeof(5*int),因此&a+1指向的地址为&a[5](数组a[5]的下一个地址)。

如果将&a+1改为a+1,则答案为:1

标准答案:2,5 

61、以下是求一个数的平方的程序,请找出错误:
#define SQUARE(a)((a)*(a))
int a=5;
int b;
b=SQUARE(a++);

解析:因为宏在预编译的时候会将表达式展开,所以不要在宏中使用++ --方法

【标准答案】

6,30,不是预想的结果

62、这段代码执行有什么问题?

#define Max_CB 500
void LmiQueryCSmd(StructMSgCB * pmsg)
{
unsigned char ucCmdNum;
......  
for(ucCmdNum=0;ucCmdNum<Max_CB;ucCmdN
um++)
{
......;
}   

解析:

unsigned char的取值范围为:0-255,而Max_CB最大为500,ucCmdNum无法达到。

【标准答案】

死循环

63、嵌入式系统中经常要用到无限循环,你怎么用C编写死循环。

【标准答案】

while(1)/for(; ; ;)

64、不能做switch()的参数类型是什么?

【标准答案】

浮点型

65、一句代码实现x是否为2的若干次幂的判断。

解析:2的n次方可以得到x,则x为2的若干次幂。

2的n次方,从二进制角度看,只占一位,减1后再相与,则结果为0,

如果非2的若干次幂,相与则结果为1.

【标准答案】

return x&(x-1)?0:1

66、中断是嵌入式系统中重要的组成部分,这导致了很多编译开发商提
供一种扩展—让标准C 支持中断。具代表事实是,产生了一个新的关键字
__interrupt 。下面的代码就使用了__interrupt 关键字去定义了一个中断服
务子程序(ISR),请评论一下这段代码的。

__interrupt double compute_area (double radius)
{
        double area = PI * radius * radius;
        printf(" Area = %f", area);
        return area;
}

【标准答案】

1) ISR 不能返回一个值。如果你不懂这个,那么你不会被雇用的。
2) ISR 不能传递参数。如果你没有看到这一点,你被雇用的机会等同第
一项。
3) ISR 应该是短而有效率的,在ISR 中做浮点运算是不明
智的

4)printf() 经常有重入和性能上的问题,不可重入函数不可以在它还没有返回就再次被调用。例如printf,malloc,free等都是不可重入函数。

67、下面的代码输出是什么,为什么?

void foo(void)
{
        unsigned int a = 6;
        int b = -20;
        (a+b> 6)? puts("> 6") : puts("<= 6");
}

解释:

当表达式中存在有符号类型和无符号类型时,所有的数都自动转换为无符号类型。因此-20 变成了一个非常大的正整数,所以该表达式计算出的结果大于6 。

【标准答案】

输出>6

68、下面的代码片段的输出是什么,为什么?

char *ptr;
if ((ptr = (char *)malloc(0)) == NULL)
puts("Gota null pointer");
else
puts("Gota valid pointer");

解释:

malloc(0)是指分配内存大小为零
NULL是不指向任何实体
malloc(0)也是一种存在不是NULL

但是如果对其进行读写操作则会报错

【标准答案】

Got   a   valid   pointer

69、编写strcpy 函数
已知strcpy 函数的原型是 char *strcpy(char *strDest, 
const char *strSrc);其中strDest是目的字符串,
strSrc 是源字符串。
(1)不调用C++/C 的字符串库函数,请编写函数
strcpy 。
(2)strcpy 能把 strSrc 的内容复制到strDest,为什
么还要char *  类型的返回值?

【标准答案】

char *strcpy(char *strDest, const char *strSrc) {
        assert((strDest != NuLL)&&(strSrc != NULL));
        char* ret = strDest;
        while((*strDest++ = *strSrc++) != '\0');
        return ret;
}

1) 注意编程风格,使用strDest, strSrc 这样增强可读性的名字。

2) 使用断言来检验输入参数的有效性,如果没有对传入参数strDest和strSrc进行检查,一但它们中有一个为NULL,立死!  assert宏的原型定义在<assert.h>中,其作用是如果它的条件返回false,则终止程序执行。可以在任何时候启用和禁用断言验证,因此可以在测试时启用断言,而在部署时禁用断言。

3) 使用const来约束strSrc,提高程序的健壮性。如果函数体内的语句试图改动strSrc的内容,编译器将指出错误。

4) strcpy能把strSrc的内容复制到strDest;为什么还需要char *类型的返回值?——为了支持链式操作

70、排序法,从小到大

【标准答案】

使用冒泡法——就是在每一轮的筛选中找出当前元素组中最大的那个数
在不断的交换中将当前元素组中最大的数赋值给当前元素组中最后一个元素空间

当然除了冒泡法,还有各类排序法。

#include <stdio.h>
int main()
{
int a[10]={6,7,8,9,10,1,2,3,4,5};
int tmp;
for (int i=0;i<10;i++)
{
	for (int j=0;j<10-i;j++)
	{
		if(a[j]>a[j+1])
		{
			tmp=a[j];
			a[j]=a[j+1];
			a[j+1]=tmp;
		}
	}
}
for(int i=0;i<10;i++)
{
	printf("out is: %d\n",a[i]);
}

return 0;
}

71、写出二分查找的代码

解释:

注意:该算法的使用的前提是静态查找表中的数据必须是有序的。

example:

对静态查找表{5,13,19,21,37,56,64,75,80,88,92}采用折半查找算法查找关键字为 21 的过程为:
 

                                                       图 1 折半查找的过程(a)


如上图 1 所示,指针 low 和 high 分别指向查找表的第一个关键字和最后一个关键字,指针 mid 指向处于 low 和 high 指针中间位置的关键字。在查找的过程中每次都同 mid 指向的关键字进行比较,由于整个表中的数据是有序的,因此在比较之后就可以知道要查找的关键字的大致位置。

例如在查找关键字 21 时,首先同 56 作比较,由于21 < 56,而且这个查找表是按照升序进行排序的,所以可以判定如果静态查找表中有 21 这个关键字,就一定存在于 low 和 mid 指向的区域中间。

因此,再次遍历时需要更新 high 指针和 mid 指针的位置,令 high 指针移动到 mid 指针的左侧一个位置上,同时令 mid 重新指向 low 指针和 high 指针的中间位置。如图 2 所示:

 

                                                        图 2 折半查找的过程(b)

同样,用 21 同 mid 指针指向的 19 作比较,19 < 21,所以可以判定 21 如果存在,肯定处于 mid 和 high 指向的区域中。所以令 low 指向 mid 右侧一个位置上,同时更新 mid 的位置。
 

                                                         图 3 折半查找的过程(3)

当第三次做判断时,发现 mid 就是关键字 21 ,查找结束。

【标准答案】

#include <stdio.h>
#include <string.h>
int main()
{
	int b[11]={5, 13, 19, 21, 37, 56, 64, 75, 80, 88, 92};
	int a[11];
	for(int i=1;i<12;i++)
	{
		a[i]=b[i-1];//因为low从1开始,所以二分法的数组也要从1开始
	}
	int low=1;//初始状态 low 指针指向第一个关键字
	int high=sizeof(a)/sizeof(a[0]);//high 指向最后一个关键字
	int middle=0;
	int key=5;
	printf("lenght is:%d\n", high);
	while(low<=high)
	{
		middle=(low+high)/2;//int 本身为整形,所以,mid 每次为取整的整数
		if(a[middle] == key)//如果 mid 指向的同要查找的相等,返回 mid 所指向的位置
		{
			printf("find location is: %d", middle);
			break;
		} else if(a[middle]>key)//如果mid指向的关键字较大,则更新 high 指针的位置
	    {
			high=middle-1;
		} else //反之,则更新 low 指针的位置
		{
			low=middle+1;
		}
	}
	return 0;
}

72、请编写一个C函数,该函数输出一个字节中被置1的位的个数。

解释:

必须要让char类型的a转换之后赋值给int类型的ret,因为char和int类型同时处理的情况下,需要将char型转换为int类型,以便计算。

【标准答案】

#include <stdio.h>

int main()
{
   char a='b';
   scanf("input is: %c", &a);
   printf("output is: %c\n", a);
   int num=0;
   int ret=0;
   for(int i=0;i<8;i++)
   {
	    ret=a>>i;
   		if((ret)&0x01)
		{
			num++;
		}
   }
   printf("num is: %d\n", num);
   return 0;
}

73、请编写一个C 函数,该函数将给定的一个字符串转换成整数。

【标准答案】

给出实现流程,然后转换成函数即可

还有需要增加优化的是:负数,含有字符的判断

#include <stdio.h>
#include <stdbool.h>
#include <stdlib.h>

int main()
{
    const char *str="568541";
	int *a;
	printf("char is: %s\n", str);
	int num=0;
	while(*str != '\0')
	{
		printf("test char is: %c\n", *str);
		*a=*str-48;
		printf("test int is: %d\n", *a);
		num=num*10+*a;
		a++;
		str++;	
	}
	printf("int num is: %d\n", num);
	num=0-num;//转换为负数最简单的方法
	printf("int -num is: %d\n", num);
    return 0;
}

74、请编写一个C 函数,该函数将给定的一个整数转换成字符串。

【标准答案】

#include <stdio.h>
#include <stdbool.h>
#include <stdlib.h>

int main()
{
    int test=56854;
	char a[100];
	printf("int is: %d\n", test);
	int num=0;
	int tmp=0;
	int stl=0;
	int stl_cp=test;
	while(stl_cp != 0)
	{
		stl_cp=stl_cp/10;
		stl++;
	}//计算test有多少位数
	printf("le is: %d\n", stl);
	for(int i=stl-1; i>=0; i--)
	{
		num=test%10;//取最后一位
		test=test/10;//依次减少一位数
		tmp=num+48;//数字转为字符
		a[i]=(char)(tmp);
		//a++;
	}
	printf("char is: %s\n", a);
    return 0;
}

75、实现strcmp 函数

【标准答案】

#include <stdio.h>
#include <string.h>
int strcmp_model(char* str1, char* str2)
{
   int tmp=0;
   int flag=0;
   int str1_len=strlen(str1);
   int str2_len=strlen(str2);
   printf("len is: %d,%d\n", str1_len,str2_len);
   if(str1_len <= str2_len) {
		tmp=str1_len;
   }else{
   		tmp=str2_len;
   } //选择最小的数
   for(int i=0;i<tmp;i++) {
   		int a=(int)(*str1-48); //转为整数量进行判断
	    int b=(int)(*str2-48);
	    if(a>b) {
			flag=1;
			return 1;
			break;
		} else if(a<b){
			flag=1;
			return -1;
			break;
		}
	    str1++;
	    str2++;
   }
   if(flag != 1) {
	    return 0;
   }	
}
int main ()
{
   char *str1="adefg";
   char *str2="adcd";
   int ret=strcmp_model(str1, str2);
   if(ret>0) {
   		printf("str1>str2\n");
   } else if(ret<0) {
		printf("str1<str2\n");   
   } else {
   		printf("str1=str2\n");
   }
   return(0);
}

76、请编写一个C 函数,该函数将一个字符串逆序。

【标准答案】

#include <stdio.h>
#include <string.h>
#include <assert.h>
void back_str(char *dest, char *src)
{
	assert((dest != NULL) && (src != NULL));
	int stl = strlen(dest);
	for(int i=0;i<stl;i++)
	{
		src[i] = dest[stl-1-i];
	}
}

int main()
{
	char *a="abcdefg";
	char b[100];
	back_str(a,b);
	printf("output is: %s", b);
	return 0;
}

77、请编写一个C 函数,该函数在给定的内存区域搜索给定的字符,并返回该字符所在位置索引值。

【标准答案】

#include <stdio.h>
#include <string.h>
#include <assert.h>

int find_str(char *src, int region, char type)
{
	int i=0;
	while(i<region)//在区域范围内寻找
	{
		//printf("str is: %c.\n", *a);
		//printf("type is: %c.\n", type);
		if(*src == type)//判断是否相等
		{
			//printf("location is: %d.", i+1);
			return i+1;
			break;
		}
		src++;
		i++;
	}
}

int main()
{
	char *a="abcdfg";
	int region=5;
	char type='d';
	int ret=find_str(a,region,type);
	printf("location is: %d", ret);
}

78、请编写一个C函数,该函数在一个字符串中找到可能的最长的子字符串,该子字符串是由同一字符组成的。

【标准答案】

#include <stdio.h>
#include <string.h>
#include <assert.h>

int main()
{
	char *a="abcddddddffffggg";
	char tmp;
	int b=1;
	int max=1;
	while(*a != '\0')
	{
		printf("output char is: %c\n", *a);
		if(tmp == *a)//判断这次和上一次是否相等
		{
			b++;
		} else { //如果不相等,那么相等的情况就已经断了
			if(b > max)  //如果大于最大值,则重新赋值
			{
				max=b;
			}
			b=1;
		}
		tmp=*a;
		a++;
	}
	printf("num is: %d", max);
}

79、有一浮点型数组A, 用C语言写一函数实现对浮点数组A进行降序排序,并输出结果,要求要以数组A作为函数的入口.

【参考答案】使用的是冒泡法

#include <stdio.h>
#include <string.h>
#include <assert.h>

int main()
{
	float num[] = {3.2, 3.3, 3.1, 2.1, 2.5, 4.2, 4.1, 4.3};
	float tmp = 0.0f;
	int stl = sizeof(num)/sizeof(*num);
	printf("stl is: %d\n", stl);
	for(int i=0;i<stl;i++)
	{
		for(int j=0;j<stl-i-1;j++)
		{
			if(num[j] > num[j+1])
			{
				tmp = num[j+1];
				num[j+1] = num[j];
				num[j] = tmp;
			}
		}
	}
	for(int i=0;i<stl;i++)
	{
		printf("output is: %f\n", num[i]);
	}
}

80、将二维数组行列元素互换,存到另一个数组中。

【参考答案】

#include <stdio.h>
#include <string.h>
#include <assert.h>

int main()
{
	int num[3][4] = {{1,2,3,5},{4,5,6,2},{7,8,9,1}};
	int num1[4][3];
	int row = sizeof(num)/sizeof(num[0]);
	int col = sizeof(num[0])/sizeof(num[0][0]);
	printf("row and col is: %d, %d\n", row, col);
	for(int i=0;i<row;i++) //行
	{
		for(int j=0;j<col;j++) //列
		{
			num1[j][i]=num[i][j];
		}
	}

	for(int i=0;i<row;i++)
	{
		for(int j=0;j<col;j++)
		{
			printf("output raw is : %d\n", num[i][j]);
			printf("output back is : %d\n", num1[i][j]);
		}
	}
	
}

81、输入一行字符,统计其中有多少个单词。

【参考答案】

#include <stdio.h>
#include <string.h>
#include <assert.h>

int main()
{
	char *str = "nijunchao sds 2323";
	int flag=0;
	printf("str size is: %d\n", strlen(str));
	while(*str != '\0')
	{
		if((*str>='a')&&(*str<='z') || (*str>='A')&&(*str<='Z'))
		{
			flag++;
		}
		str++;
	}
	printf("character num is: %d\n", flag);
}

82、写一个内存拷贝函数,不用任何库函数.

【参考答案】

#include <stdio.h>
#include <string.h>
#include <assert.h>

void* str_cp(char *dest, char *src)
{
	int len = strlen(src);
	int i=0;
	//printf("len is: %d", len);
	while(*src != '\0')
	{
		dest[i] = *src;
		src++;
		i++;
	}
	return dest;
}

int main()
{
	char *str1 = "abcdefg";
	char str[100];
	char *ret;
	ret = str_cp(str1,str);
	printf("ret is: %s", ret);
	return 0;
}

83、有1、2、3 、4 ,能组成多少个互不相同且无重复数字的三位数?都是多少?

【参考答案】

#include <stdio.h>
#include <string.h>
#include <assert.h>

int main()
{
	//1,2,3,4
	int a[4]={1,2,3,4};

	for(int i=1;i<5;i++)
	{
		for(int j=1;j<5;j++)
		{
			for(int k=1;k<5;k++)
			{
				if((i!=j)&&(j!=k)&&(i!=k))//输出互不相等的情况
				{
					printf("num is: %d%d%d\n", i,j,k); //可以设置一个++统计
				}
			}
		}
	}
	return 0;
}

84、取一个整数a从右端开始的4~7位。

【参考答案】

#include <stdio.h>
#include <string.h>
#include <assert.h>

int main()
{
	int a=0x578;
	int b=a>>4;
	int c=b&0x0f;
	printf("output is: %d", c);
	return 0;
}

85、打印出杨辉三角形

【参考答案】

86、实现strcmp 函数。

【参考答案】

与75题类似

87、写一个函数,求一个字符串的长度,在main函数中输入字符串,并输出其长度。

【参考答案】

这个简单,判断字符串什么时候到 '\0' 为止。

88、809*??=800*??+9*?? 其中??代表的两位数, 809*??为四位数,8*??的结果为两位数,9*??的结果为3位数。求??代表的两位数,及809*??后的结果。

【参考答案】

#include <stdio.h>

int main()
{
   //  Write C code in this online editor and run it.
   for(int i=10;i<=99;i++)
   {
	   	int a=809*i;
	    int b=800*i+9*i;
   		if((809*i>999) && ((i*8)<=99) && ((i*9)>99) && (a==b))
		{
			printf("i is: %d.\n", i);
		}
   }
   printf("Hello, World! \n");
   
   return 0;
}

89、某个公司采用公用电话传递数据,数据是四位的整数,在传递过程中是加密的,加密规则如下:每位数字都加上5, 然后用和除以10的余数代替该数字,再将第一位和第四位交换,第二位和第三位交换。

【参考答案】

#include <stdio.h>

int main()
{
	
	int a=1234;
	int num[10],out[10];
	int len=4;
	int i=0;
	int b=0;
	int change=0;
	while(len)
	{
		b=a%10;
		a=a/10;
		num[i]=b;
		len--;
		i++;
	}//拆解得到每一个数据
	for(int i=0;i<4;i++)
	{
		printf("output is: %d\n", num[i]);
	}
	out[0]=(num[0]+5)%10;
	out[1]=(num[1]+5)%10;
	out[2]=(num[2]+5)%10;
	out[3]=(num[3]+5)%10;
	printf("ret is: %d%d%d%d\n", out[0],out[1],out[2],out[3]);
	
	return 0;
}

90、计算字符串中子串出现的次数。

【参考答案】

#include <stdio.h>
#include <string.h>

int main()
{
   int flag=0;
   int count=0;
   char *parent = "sdfrabckdh rgabcjg hrabcjd  hrrabcabc";
   char *child  = "abc";
   printf("parent lengh is: %d\n", strlen(parent));
   printf("child lengh is: %d\n", strlen(child));
   for(int i=0;i<strlen(parent);i++)
   {
   		for(int j=0;j<strlen(child);j++)
		{
			if(parent[i+j] == child[j])
			{
				flag++;	
			}
		}
	    if(flag == strlen(child))
		{
			flag = 0;
			count++;
		}
   } 
   printf("the count is: %d\n", count);
   return 0;
}

91、怎么判断链表中是否有环?(需要继续探究链表)
【参考答案】

快慢指针法 

用两个指针来遍历这个单向链表,第一个指针p1,每次走一步;第二个指针p2,每次走两步;当p2 指针追上p1的时候,就表明链表当中有环路了。

92、实现双向链表删除一个节点P,在节点P 后插入一个节点,写出这两个函数。

【参考答案】

和单向链表的方法类似但是有不同,如删除操作,单向链表需要增加一个postNodeFront链表,而双向链表不需要。

数据结构-双链表-C语言实现_哔哩哔哩_bilibili

93、把一个链表反向。

【解释】

了解链表:

1个小时学会单链表,C语言数据结构专题_哔哩哔哩_bilibili

单链表逆序_autumn20080101的专栏-CSDN博客_单链表逆转

我们先定义三个指针,一个指向当前节点 curr,一个指向当前节点的前一个节点 pre,一个指向当前节点的后一个节点 pnext。当遍历到某个节点curr 时,把 pnext 指针指向 curr 的下一个节点 curr->next,即记住了当前节点的下一个节点(防止断链),然后开始做指针反向操作,把 curr->next 反过来指向其前一个节点 pre,这一步已经在原地完成了当前节点的反转,接下来移动指针继续完成和上面同样的操作,即把 pre 移动到 curr,把 curr 移动到 pnext,直到所有的节点都被遍历完。

有了思路之后,我们实现一个单链表

#include <stdio.h>
#include <stdlib.h>

struct Node
{
	int data;
	struct Node *next;
};

struct Node* createList()
{
	struct Node* headNode = (struct Node*)malloc(sizeof(struct Node));
	headNode->next = NULL;
	return headNode;
}

struct Node* createNode(int data)
{
	struct Node* newNode = (struct Node*)malloc(sizeof(struct Node));
	newNode->data = data;
	newNode->next = NULL;
	return newNode;
}
//头插法
void insertNodebyHead(struct Node* headList, int data)
{
	struct Node *newNode = createNode(data);
	newNode->next = headList->next;
	headList->next = newNode;
}
//尾插法
void insertNodebyRear(struct Node* headList, int data)
{
	struct Node *newNode = createNode(data);
	while(headList->next)//找到末尾
	{
		headList = headList->next;
	}
	newNode->next = NULL;//插入即可
	headList->next = newNode;
}
void printNode(struct Node* HeadNode)
{
	struct Node* pMove = HeadNode->next;
	while(pMove)
	{
		printf("node data is: %d\n", pMove->data);
		pMove = pMove->next;
	}
}

void deleteNodebyAppoint(struct Node* HeadNode, int data)
{
	struct Node* postNodeFront = HeadNode;
	struct Node* postNode = HeadNode->next;
	while(postNode->data != data)
	{
		postNodeFront = postNode;
		postNode = postNodeFront->next;
		if(postNode == NULL)
		{
			printf("not find data\n");
			return;
		}
	}
	postNodeFront->next = postNode->next;
	free(postNode);
}

int main()
{
   /*  Write C code in this online editor and run it. */
   printf("Hello, World! \n");
   struct Node* list = createList();
   insertNodebyRear(list, 1);
   insertNodebyRear(list, 2);
   insertNodebyRear(list, 3);
   insertNodebyRear(list, 4);
   printNode(list);
   deleteNodebyAppoint(list,5);
   deleteNodebyAppoint(list, 3);
   printNode(list);
   return 0;
}

【参考答案】

启动逆序时候的链表初始状态:

最终的迭代状态:(所以逆序函数中返回的是pre)

struct Node* reverseLits(struct Node* head)
{
	struct Node* next = NULL;
	struct Node* pre = NULL;
    while (head != NULL) {
        //在头节点改变之前,先获取下一个节点的指针
        next = head->Next;
        //头节点的下一个节点要改成它的上一个节点,是一个逆转的过程
        head->Next = prev;
        //上一个节点前移指向头节点
        prev = head;
        //头节点前移指向下一个节点
        head = next;
    }
	return pre;
}

94、C语言实现:合并两个有序的数组,合并后的数组依然有序(并且去重) 

【解释】

分成两部分来做,1)合并有序数组;2)合并之后的有序数组去重

合并参考:C语言实现:合并两个有序的数组,合并后的数组依然有序_dangzhangjing97的博客-CSDN博客_合并两个有序数组c语言

(注意:该参考中,剩余元素要使用while循环去赋剩余值)

去重参考:

【LeetCode】数组问题:快慢指针_菜鸡也有大佬梦-CSDN博客

【参考答案】

#include <stdio.h>
#include <string.h>
int main()
{
   /*  Write C code in this online editor and run it. */
   printf("Hello, World! \n");
   int test1[9]={1,3,5,7,13,16,17,19,30};
   int test2[9]={2,4,7,8,9,12,18,20,30};
   int length1=sizeof(test1)/sizeof(int);
   int length2=sizeof(test2)/sizeof(int);
   printf("length1 is: %d\n", length1);
   printf("length2 is: %d\n", length2);
   int test3[length1+length2];
   int index1=0;
   int index2=0;
   int index=0;
   int slow=0;
   int i=0;
   while((index1 < length1) && (index2 < length2))
   {
		if(test1[index1] < test2[index2]) {
			test3[index] = test1[index1];
			index1++;
		} else {
			test3[index] = test2[index2];
			index2++;
		}
	    index++; 
   }
   while(test1[index1])
   {
	    test3[index] = test1[index1];
   		index1++;
	    index++;
   }
   while(test2[index2])
   {
	    test3[index] = test2[index2];
   		index2++;
	    index++;
   }

   for(slow=0; slow<length1+length2-1; slow++)//快慢法去重复
   {
   		if(test3[slow] != test3[slow+1])
		{
			i++;
			test3[i] = test3[slow+1];
		}
   }
   printf("i is: %d\n", i);
	
   for(int t=0;t<i+1; t++)
   {
   		printf("test3 is: %d\n", test3[t]);
   }
   return 0;
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值