1.sizeof和strlen的区别
sizeof和strlen有以下区别:
sizeof是一个操作符,strlen是库函数。
sizeof的参数可以是数据的类型,也可以是变量,而strlen只能以结尾为‘\0‘的字符串作参数。
编译器在编译时就计算出了sizeof的结果。而strlen函数必须在运行时才能计算出来。
sizeof计算的是数据类型占内存的大小,而strlen计算的是字符串实际的长度。
数组做sizeof的参数不退化,传递给strlen就退化为指针了。
2.说明关键字volatile有什么含意
volatile的作用:
作为指令关键字,确保本条指令不会因编译器的优化而省略,且要求每次直接读值。
一个定义为volatile的变量就是说这个变量可能会被意想不到的改变,这样,编译器就不会去随便假设这个变量的值了。
精确的说,优化器在用到这个变量的值的时候,必须每次都小心的重新读取这个变量的值,而不是使用保存在寄存器里面的备份。
下面是volatile变量的几个例子:
1). 并行设备的硬件寄存器(如:状态寄存器)
2). 一个中断服务子程序中会访问到的非自动变量(Non-automatic variables)
3). 多线程应用中被几个任务共享的变量
3.写一个“标准”宏MIN
#define min(a,b) ( (a)<=(b)?(a):(b) )
注意:在调用时一定要注意这个宏定义的副作用,如下调用:
min(++*p,x) 展开为 ((++*p)<=(x)?(++*p):(x)。
p指针就自加了两次,违背了MIN的本意。
4.设置地址为0x67a9的整型变量的值为0xaa66
*(int *)0x67a9 = 0xaa66;
5.用变量a定义
一个整型数 int a;
一个指向整型数的指针 int *a;
一个指向指针的指针,它指向的指针是指向一个整型数 int **a;
一个有10个整型数的数组 int a[10];
一个有10指针的数组,该指针是指向一个整型数 int *a[10];
一个指向有10个整型数数组的指针 int (*a)[10];
一个指向函数的指针,该函数有一个整型数参数并返回一个整型数 int (*a)(int);
一个有10个指针的数组,该指针指向一个函数,该函数有一个整型数参数并返回一个整型 int (*a[10])(int);
6.static的用法(定义和用途)
1)用static修饰局部变量:使其变为静态存储方式(静态数据区),那么这个局部变量在函数执行完成之后不会被释放,而是继续保留在内存中。
2)用static修饰全局变量:使其只在本文件内部有效,而其他文件不可连接或引用该变量。
3)用static修饰函数:对函数的连接方式产生影响,使得函数只在本文件内部有效,对其他文件是不可见的。
这样的函数又叫作静态函数。使用静态函数的好处是,不用担心与其他文件的同名函数产生干扰,另外也是对函数本身的一种保护机制。
7.const的用法(定义和用途)
const主要用来修饰变量、函数形参和类成员函数:
1)用const修饰常量:定义时就初始化,以后不能更改。
2)用const修饰形参:func(const int a){};该形参在函数里不能改变
3)用const修饰类成员函数:该函数对成员变量只能进行只读操作,就是const类成员函数是不能修改成员变量的数值的。
被const修饰的东西都受到强制保护,可以预防意外的变动,能提高程序的健壮性。
const int a;
int const a;
const int *a; //等于 int const *a;
int * const a;
int const * const a; //等于 const int* const a;
前两个的作用是一样,a是一个常整型数。
第三个意味着a是一个指向常整型数的指针(也就是,整型数是不可修改的,但指针可以)。
第四个意思a是一个指向整型数的常指针(也就是说,指针指向的整型数是可以修改的,但指针是不可修改的)。
最后一个意味着a是一个指向常整型数的常指针(也就是说,指针指向的整型数是不可修改的,同时指针也是不可修改的)。
8.内存四区
由C/C++编译的程序占用的内存分为以下几个部分
8.1.代码区:存放函数体内的二进制代码,有操作系统管理。
8.2.全局区:存放全局变量,静态变量以及常量。
这部分可以细分为data区和bss区
(1)data区
data区里主要存放的是已经初始化的全局变量、静态变量和常量
(2)bss区
bss区主要存放的是未初始化的全局变量、静态变量,这些未初始化的数据在程序执行前会自动被系统初始化为0或者NULL
(3)常量区
常量区是全局区中划分的一个小区域,里面存放的是常量,如const修饰的全局变量、字符串常量等
8.3.栈区:由编译器自动分配和释放,存放函数参数,局部变量等
8.4.堆区:由程序员分配释放,若程序员未释放,则程序结束之后,编译器自动释放。
注意:
const修饰的全局变量也储存在常量区;
const修饰的局部变量依然在栈上。
有的分为5个区,即代码区,全局区,常量区,栈区,堆区
#include<stdio.h>
#include<malloc.h>
int a = 0; //全局初始化区 .data段
char *p1; //全局未初始化区 .bss段
void main()
{
int b;// 栈
char s[] = "abc"; //s在栈上,大小4字节。 "abc\0"为s的初始值,可以不用纠结其存在哪里
char *p2; //栈
char *p3 = "123456"; //"123456\0"在常量区,p3在栈上。
static int c =0; //全局(静态)初始化区 .data段
p1 = (char *)malloc(10);
p2 = (char *)malloc(20);
//分配得来的10和20字节的区域就在堆区。
strcpy(p1, "123456"); //"123456\0"放在常量区,编译器可能会将它与p3所指向的"123456"优化成一个地方。
}
9.大小端问题
大端:一个多字节整数,数字的高位部分存放在内存的低地址单元。低地址存高字节
小端:一个多字节整数,数字的低位部分存放在内存的低地址单元。低地址存低字节
大小端格式问题查看:
方法一:
void checkCpuMode(void)
{
int i = 0x12345678;
char *cp = (char *)&i;
if(*cp == 0x78)
printf("little endian");
else
printf("big endian\n");
}
方法二:
void checkCpuMode(void)
{
int a = 0x12345678;
if((char)a == 0x12)
printf("big endian\n");
else
printf("little endian\n");
}
方法三:
void checkCpuMode(void)
{
union
{
short s;
char c[sizeof(short)];
}un;
un.s=0x0102;
if(un.[0]==1&&un.c[1]==2)
printf("big endian\n");
else
printf("little endian\n");
}
10.静态局部变量区别
(1)
① static局部变量在编译阶段就已经分配内存空间了,也就是函数没有调用前它就已经存在了。
② 普通局部变量只有运行到定义该变量的时候才会分配内存空间。
(2)
① 当执行完定义该static局部变量的函数体后,该static变量的内存空间不会被释放,只有程序结束时static变量才会自动释放。
② 当执行完定义该普通局部变量的函数体后,该普通局部变量的内存空间就会被释放。
(3)
① 如果static局部变量不初始化,那么它默认为0。
② 如果普通局部变量不初始化,那么它的值为随机数。
(4)
① static局部变量的初始化语句只会执行一次,下次再运行该初始化语句就不会被初始化,但是它可以被多次赋值。
② 每次运行该普通局部变量的定义时该变量都会被初始化。
11.#define宏定义与typedef的区别
#define宏定义是字符替换,typedef是定义类型,是声明一种新的类型,等同自带的基本类型。
#define是宏,处理的时候位于编译前阶段,宏处理器基本上对你的C/C++程序不会有任何的感知。
typedef是编译阶段的一部分。
12.sizeof与strlen计算
(1)sizeof值计算
char str1[] = {'a', 'b', 'c', 'd', 'e'};
char str2[] = "abcde";
char *ptr = "abcde";
char book[][80]={"计算机应用基础","C语言","C++程序设计","数据结构"};
sizeof(str1)=?
sizeof(str2)=?
sizeof(ptr)=?
sizeof(book)=?
sizeof(book[0])=?
分析:
sizeof(str1)=5,就是5*sizeof(char)=5;
sizeof(str2)=6,字符串都是以’0’结尾,所以所占字节数为6;
sizeof(ptr)=4,ptr是一个指针,在32位平台上大小为4字节;
sizeof(book)=320,book是一个二维数组,4*80*1
sizeof(book[0])=80,book[0]是第一维数组,因为此80*1
根据sizeof求数组元素的个数也很简单,拿第一个来说,就是sizeof(str1)/sizeof(char)。
(2)计算strlen的值
char arryA[] = {'a','b','c',0,'d','e'};
char arryB[] = {'a','b','c','d','e'};
char arryC[6] = {'a','b','c','d','e'};
char *str = "abcde";
分析:
strlen(arryA) = 3,strlen遇到’\0’就会返回,无论后面有多少个字符;
strlen(arryB)长度无法确定,没有人为写入‘\0’,strlen会继续计算直到找到结束符,结果未知;
strlen(arryC)=5,指定了数组大小,编译器会自动在空余地方添加’\0’,这其实跟char arryC[6] = {‘a’,‘b’,‘c’,‘d’,‘e’,‘\0’};等价。
strlen(str) = 5,不包括结尾的’\0’。
13.char str[]和char *str
1)下面的操作合法么?出错的话,会是在那个阶段?编译时期还是运行时期?
char str[] = "hello";
str[0] = 's'; //合法么
char *str = "hello";
str[0] = 's'; //合法么
分析:
这两个都可以成功编译,只是第二个会在运行时期出现段错误。下面来分析一下:
首先"hello"是一个字符串常量,存储在静态数据区域(data段),这是在编译时期就确定的。第一个是将字符串常量赋值给了一个变量(全局变量在数据段,局部变量在栈区),
实际上是将字符串常量拷贝到了变量内存中,因此修改的只是str[]这个变量的值。
第二个是将字符串常量的首地址赋值给指针str,对指针str操作就是对字符串常量进行修改!因此出现了段错误。
(2)理解了上面的知识,判断一下下面的true or false?
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;
cout << ( str3 == str4 ) << endl;
cout << ( str5 == str6 ) << endl;
cout << ( str7 == str8 ) << endl;
分析:
结果是: 0 0 1 1
先理解str1,str2,str3,str4,他们是什么?他们是数组名,也就是数组首元素的地址!”str1 == str2“本质就是比较两个数组的地址是不是相同。
上面我们说过,编译器给他们分配了新的存储空间来对字符串"abc"进行拷贝,这些变量在内存里是相互独立的,因此他们的地址肯定不同!
再理解str5,str6,str7,str8,他们是什么?他们是指针,他们的值就是字符串常量的地址!它们都指向“abc"所在的静态数据区,所以他们都相等。
(3)数组作为函数参数传递
看看下面的函数有啥问题:
int func(int a[])
{
int n = sizeof(a)/sizeof(int);
for(int i=0;i<n;i++)
{
printf("%d ",a[i]);
a[i]++;
}
}
sizeof(a)/sizeof(int)一般是计算数组的元素个数,结果却发现n的值总是1,为什么会这样呢?这是因为在C中,将数组传递给一个函数时,无法按值传递,而是会自动退化为指针。
下面的三种写法其实是等价的:
“int func(int a[20]);” 等价于 “int func(int a[]);” 等价于 “int func(int *a);”。
14.数组指针和指针数组的问题
(1)说出下面表达式的含义?
int *p1[10];
int (*p2)[10];
第一个是指针数组,首先他是一个数组,数组的元素都是指针。
第二个是数组指针,首先他是一个指针,它指向一个数组。
(2)写出下面程序运行的结果
int a[5] = { 1, 2, 3, 4, 5 };
int *ptr = (int *)(&a + 1);
printf("%d,%d", *(a + 1), *(ptr - 1));
分析:
答案是2,5。本题的关键是理解指针运算,”+1“就是偏移量的问题:一个类型为T的指针移动,是以sizeof(T)为单位移动的。
a+1:在数组首元素地址的基础上,偏移一个sizeof(a[0])单位。因此a+1就代表数组第1个元素,为2;
&a+1:在数组首元素的基础上,偏移一个sizeof(a)单位,&a其实就是一个数组指针,类型为int()[5]。因此&a+1实际上是偏移了5个元素的长度,也就是a+5;再看ptr是int类型,因此"ptr-1"就是减去sizeof(int*),即为a[4]=5;
a是数组首地址,也就是a[0]的地址,a+1是数组下一个元素的地址,即a[1]; &a是对象的首地址,&a+1是下一个对象的地址,即a[5]。
15.c语言char和float相互转换方法
作用:可以灵活调整float类型的字节序
union CharFloat{
float numeric;
unsigned char ascii[4];
};
float charTofloat(unsigned char *str) {
CharFloat value;
value.ascii[0] = str[0];
value.ascii[1] = str[1];
value.ascii[2] = str[2];
value.ascii[3] = str[3];
return value.numeric;
}
void floatochar(char* returnData, float numeric) {
CharFloat value;
int i = 0;
value.numeric = numeric;
returnData[i++] = value.ascii[3];
returnData[i++] = value.ascii[2];
returnData[i++] = value.ascii[1];
returnData[i++] = value.ascii[0];
for(int j=0;j<4;j++)
printf("%2x,",value.ascii[j]);
}