1.以下是32 位系统上C 程序,请计算sizeof 的
值。
程序一:
char str[] = “ Hello” ;
char *p = str ;
i nt n = 10;
请计算
(1)sizeof (str ) = 6 (2)s i zeof ( p ) = 4
(3)sizeof ( n ) = 4
程序二:
void Func ( char str[100])
{
…… ;
}
请计算sizeof( str ) = 4
程序三:
void * p = malloc( 100 );
请计算sizeof ( p ) = 4
2. 用变量a给出下面的定义
(1)一个有10个指针的数组,该指针是指向一个整型数
的;
====》 int * a[10];
(2) 一个指向有10个整型数数组的指针;
====》 int (*a)[10];
(3)一个指向函数的指针,该函数有一个整型参数并返回一个整型数;
====》int (*a)(int);
(4) 一个有10个指针的数组,该指针指向一个函数,该函数有一个整型参数并返回一个整型数;
====》 int (*a)[10](int);
3. 以下定义和说明
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)); 的
执行结果是:_52____
DATE是一个union, 变量公用空间. 里面最
大的变量类型是int[5], 占用20个字节. 所以它的大小是
20
data 是一个struct, 每个变量分开占用空间. 依次为int4 +
DATE20 + double8 = 32.
所以结果是20 + 32 = 52.
4.请问以下代码有什么问题
int main()
{
char a;
char *str=&a;
strcpy(str,"hello");
printf(str);
return 0;
}
=====》没有为str分配内存空间,将会发生异常问题出在将一个字符串复制进一个字符变量指针所指地址。虽然可以正确输出结果,但因为越界进行内在读写而导致程序崩溃。
5.请问以下代码有什么问题
char* s="AAA";
printf("%s",s);
s[0]='B';
printf("%s",s);
有什么错?
====》"AAA" 是字符串常量。s是指针,指向这个字符串常量,所以声明s的时候就有问题。cosnt char* s="AAA";然后又因为是常量,所以对是s[0] 的赋值操作是不合法的。
6.以下代码运行会出现什么问题
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;
} 会出现什么问题?
====》程序崩溃,getmemory中的malloc 不能返回
动态内存,free ()对str操作很危险。
7.执行以下代码会出现什么问题
char szstr[10];
strcpy(szstr,"0123456789");
产生什么结果?为什么?
=====》出现段错误,因为拷贝长度越界。
8.数组和链表的区别
====》数组:顺序存储,大小固定 链表:数据可以随机存储,大小可以动态改变
9.要对绝对地址0x100000赋值,我们可以用以下代码
(unsigned int*)0x100000 = 1234;
那么要想让程序跳转到绝对地址0x100000去执行,要如何操作?
*((void(*)())0x100000)();
首先要将0x100000强制转换成函数指针,即:
(void (*)())0x100000
然后再调用它:
*((void (*)())0x100000)();
10. volatile有什么含义
====》一个定义为volatile的变量是说这变量可能会被意想不到地改变,这样,编译器就不会去假设这个变量的值了。精确地说就是,优化器在用到这个变量时必须每次都小心地重新读取这个变量的值,而不是使用保存在寄存器里的备份。
11.嵌入式系统经常具有要求程序员去访问某特定的内存位置的特点。在某工程中,要求设置一绝对地址为0x67a9的整型变量的值为0xaa66。编译器是一个纯粹的ANSI编译器。写代码去完成这一任务。
=====》
int * ptr;
ptr= (int *)0x67a9;
*ptr = 0xaa55;
12.头文件中的ifndef/define/endif 干什么用?
=====》防止该头文件被重复引用
13.#include <filename.h> 和#include “filename.h” 有什么区别?
====》对于#include <filename.h> ,编译器从标准库路径开始搜索filename.h ;
对于#include “filename.h” ,编译器从用户的工作路径开始搜索filename.h 。
14.const 有什么用途?
=====》可以定义const 常量
const 可以修饰函数的参数、返回值,甚至函数的定义体。被const 修饰的东西都受到强制保护,可以预防意外的变动,能提高程序的健壮性。
15.、static有什么用途?
====》 限制变量的作用域(static全局变量);
设置变量的存储域(static局部变量)。
16.堆栈溢出一般是由什么原因导致的?
====》没有回收垃圾资源。
17.队列和栈有什么区别?
====》队列先进先出,栈后进先出
18.Heap与stack的差别
====》Heap是堆,stack是栈。
Stack的空间由操作系统自动分配/释放,Heap上的空间手动分配/释放。
Stack空间有限,Heap是很大的自由存储区
C 中的malloc 函数分配的内存空间即在堆上
程序在编译期对变量和函数分配内存都在栈上进行,且程序运行过程中函数调用时参数的传递也在栈上进行。
19.写一个“标准”宏,这个宏输入两个参数并返回较小的一个。
====》 #define Min(X, Y) ((X)>(Y)?(Y):(X))
20.宏定义和函数
1)宏缺乏类型检查,不如函数调用检查严格
2)宏展开可能会产生意想不到的副作用,函数一般不会
3)宏形式写的代码难以调试,难以大断点,不利于定位问题
4)宏如果调用很多,会造成代码空间的浪费,不如函数空间效率高
21.inline函数
如果编译器支持inline,可以采用inline函数,编译时不用展开,代码size小,同时可以加断点,易于定位问题
22.进程和线程的区别
进程:是并发执行的程序在执行过程中分配和管理资源的基本单位,是一个动态概念,竞争计算机系统资源的基本单位。
线程:是进程的一个执行单元,是进程内科调度实体。比进程更小的独立运行的基本单位。线程也被称为轻量级进程。
一个程序至少一个进程,一个进程至少一个线程
进程线程的区别:
- 地址空间:同一进程的线程共享本进程的地址空间,而进程之间则是独立的地址空间。
- 资源拥有:同一进程内的线程共享本进程的资源如内存、I/O、cpu等,但是进程之间的资源是独立的。
一个进程崩溃后,在保护模式下不会对其他进程产生影响,但是一个线程崩溃整个进程都死掉。所以多进程要比多线程健壮。
进程切换时,消耗的资源大,效率高。所以涉及到频繁的切换时,使用线程要好于进程。同样如果要求同时进行并且又要共享某些变量的并发操作,只能用线程不能用进程
- 执行过程:每个独立的进程程有一个程序运行的入口、顺序执行序列和程序入口。但是线程不能独立执行,必须依存在应用程序中,由应用程序提供多个线程执行控制。
- 线程是处理器调度的基本单位,但是进程不是。
- 两者均可并发执行。
23.进程和线程的优缺点
优缺点:
线程执行开销小,但是不利于资源的管理和保护。线程适合在SMP机器(双CPU系统)上运行。
进程执行开销大,但是能够很好的进行资源管理和保护。进程可以跨机器前移。
24.堆栈溢出一般是由什么原因导致的?
1)函数调用层次太深。函数递归调用时,系统要在栈中不断保存函数调用时的现场和产生的变量,如果递归调用太深,就会造成栈溢出,这时递归无法返回。再有,当函数调用层次过深时也可能导致栈无法容纳这些调用的返回地址而造成栈溢出。
2)动态申请空间使用之后没有释放。由于C语言中没有垃圾资源自动回收机制,因此,需要程序主动释放已经不再使用的动态地址空间。申请的动态空间使用的是堆空间,动态空间使用不会造成堆溢出。
3)数组访问越界。C语言没有提供数组下标越界检查,如果在程序中出现数组下标访问超出数组范围,在运行过程中可能会内存访问错误。
4)指针非法访问。指针保存了一个非法的地址,通过这样的指针访问所指向的地址时会产生内存访问错误。
25.自己实现memcpy函数
void *Memcpy(void *dst,const void *src,size_t size)
{
char *pdst=(char *)dst;
char *psrc=(char *)src;
if(dst==NULL || src==NULL)
return NULL;
//如果src和dst有重叠,则从后往前进行复制
if((src<dst) &&(char *)src+size>(char *)dst)
{
pdst=(char *)dst+size-1;
psrc=(char *)src+size-1;
while(size--)
{
*pdst--=*psrc--;
}
}
else//否则正常复制就好了
{
pdst=(char *)dst;
psrc=(char *)src;
while(size--)
{
*pdst--=*psrc--;
}
}
return dst;
}
26. 以下代码实现了平方运算,请问这段代码有什么问题
#define SQR(X) ((X)*(X))
int main(void)
{
int a=4;
int b;
b=SQR(a++);
return 0;
}
由于宏定义是整体替换,所以b=((a++)*(a++)),所以a++会先执行a*a然后再进行a++
27.以下代码是合法的吗?
int a = 5, b = 7, c;
c = a+++b;
上面的代码是合法的,但是会被编译器处理成c = a++ + b,可读性不强
28.进程间通讯方式
常见的通信方式
1)管道pipe:管道是一种半双工的通信方式,数据只能单向流动,而且只能在具有亲缘关系的进程间使用。进程的亲缘关系通常是指父子进程关系。
2)命名管道FIFO:有名管道也是半双工的通信方式,但是它允许无亲缘关系进程间的通信。
3)消息队列MessageQueue:消息队列是由消息的链表,存放在内核中并由消息队列标识符标识。消息队列克服了信号传递信息少、管道只能承载无格式字节流以及缓冲区大小受限等缺点。
4)共享存储SharedMemory:共享内存就是映射一段能被其他进程所访问的内存,这段共享内存由一个进程创建,但多个进程都可以访问。共享内存是最快的 IPC 方式,它是针对其他进程间通信方式运行效率低而专门设计的。它往往与其他通信机制,如信号量,配合使用,来实现进程间的同步和通信。
5)信号量Semaphore:信号量是一个计数器,可以用来控制多个进程对共享资源的访问。它常作为一种锁机制,防止某进程正在访问共享资源时,其他进程也访问该资源。因此,主要作为进程间以及同一进程内不同线程之间的同步手段。
6)套接字Socket:套解口也是一种进程间通信机制,与其他通信机制不同的是,它可用于不同及其间的进程通信。
7)信号 ( sinal ) : 信号是一种比较复杂的通信方式,用于通知接收进程某个事件已经发生。