深入原理64式:32 C和C++知识总结

目标:
整理C和C++知识,主要包含如下内容:
1、数组
2、字符串
3、结构体、共用体与枚举
4、运算符及其优先级
5、C预处理器与内存管理
6、函数
7、指针与引用
8、类
9、多态与虚函数表
10、泛型,模板与元编程
11、STL
12、原理
13、makefile与链接库
14、智能指针
15、effective C++
16、线程,锁与队列

第一部分 数组


1 如何定义数组的引用?如何动态声明一维数组并删除?如何动态声明二维数组并删除?
1)假设数组定义如下:
int a[6] = {0, 2, 4, 6, 8 ,10}
则定义数组的引用如下:
int (&p)[6] = a;
2)
int *p = new int[n];
delete []p;
3)
int **a = new int* [m];
for(int i = 0; i < n; i++){
    a[i] = new int [n];
}
for(int i = 0; i < n; i++){
    delete []a[i];
}
delete []a;

2 指针数组与数组指针的区别是什么?请编写与int a[4][10]相同的数组指针?请编写与int a[10]相同的数组指针和指针?
1)指针数组:是一个数组,数组中每个元素是指针。
样例: int *a[10]; 
数组指针:是一个指针,该指针指向一个数组。
样例: int (*p)[10];
记忆技巧:
因为[]的优先级高于*,所以指针必须加上括号。
针有组无
数组指针有括号,指针数组无括号。
2)
int (*p)[10];
p = a;
3)
int (*p)[10] = &a;
int *q = a;
sizeof(*p) = sizeof(a) = 40;
sizeof(*q) = sizeof(a[0]) = 4;

3 文字常量区的字符串是否可以通过指针修改?使用数组名时会自动转换为数组首元素的地址吗?
1)不可以。
char *p = "hello";
p[0] = 'x';
错误。可以通过p[0]访问,但无法修改。
2)会。但是需要注意数组首地址是常量,不能进行赋值操作。
char s[] = "hello";
s += 2;
错误,s是不可修改的。

4 高维数组相关指针如何区分?
1)假设有 int a[4][5];
                        类型            含义
a+i:                    int (*)[5]        指向a[i]第i个元素a[i]的指针,行指针
*(a+i)=a[i]:            int *            指向数组a[i]首元素a[i][0]的指针,列指针
*(*(a+i)+j)=a[i][j]:    int                因为*(a+i)类型为int*,值
&a:                        int (*)[4][5]    


第二部分 字符串


1 什么是字符串常量? 'A'与"A"是否一样?字符数组有什么特点?字符数组有哪几种初始化方式?
1)字符串常量是双引号括起来的字符序列
2) 'A'表示单个字符A,而"A"表示字符串常量,等于A和空字符null两个字符。
3) 字符数组末尾必须添加'\0'作为结束符
4) 
char ca1[] = {'C', '+', '+'};
char ca2[] = {'C', '+', '+', '\0'};
char ca3[] = "C++";
其中ca2与ca3等价,长度为4,ca1长度为3。

2 子串与子序列的区别是什么?
1)子串: 串中任意连续字符组成的组序列。
子序列不要求连续,但顺序与其主串中一致。

3 strlen的作用是什么?请实现strlen。
1)strlen计算'\0'之前的字符个数。
2)
int strlen(const char* str){
    assert (str != NULL);
    int len = 0;
    while((*str++) != '\0'){
        len++;
    }
    return len;
}
注意:
(*str++)中先计算*str,然后str++。

3 memcpy的作用是什么?strcpy与memcpy的区别是什么?memset的作用是什么?
1) 
void *mempy(void* dest, const void* src, size_t n);
从src所指的位置拷贝前n个字节到目标地址dest,返回指向dest的指针。
2) 
strcpy(dest, src): 把从src地址开始且以null结束符的字符串复制到以dest开始的地址空间。
strcpy用于字符串复制,memcpy可以用于任何内容复制,
strcpy复制整个字符串,memcpy指定复制内容的长度。
3) void *memset(void *s, int ch, size_t n)
将s中前n个字节用ch替换并返回s。

第三部分 结构体、共用体与枚举


1 结构体如何定义的?定义结构体时会分配内存空间吗?结构体赋值的限制是什么?
1) typedef struct Node{
    int data;
    Node* next;
}*List;
2)不会。只有定义结构体对象时才会分配空间。
3)结构体赋值不允许跳过前面成员给后面成员赋值。

2 结构体中的位字段是什么?结构体中对齐规则是如何的?
1)位字段是指可以对整型或枚举类型指定占用特定位数。
样例:
typedef struct reg{
    unsigned int SN:4;
    bool good:4;
}

3 共用体的特点是什么?样例是什么?共用体占用内存是怎样的?结构体占用内存是各成员内存量总和吗?
1)在同一时刻,共用体中值存放一个成员。
2) union 共用体名{
    数据类型 成员名;
    数据类型 成员名;
};
3)共用体中占用成员为各个成员占用中最大值。
4)结构体中因为可能存在内存对齐,所以占用内存>=各成员变量内存之和。

4 什么是大端存储格式?请写出0x12345678在大端和小端中分别的存储格式?十六进制和二进制之间转换规则?
低字节存放在哪里?编写如下的内存存放地址格式?
1) 大端存储格式是高字节存放在低地址中。
2) 假设地址从0x4000开始。
大端存储格式:
内存地址:    0x4000    0x4001    0x4002    0x4003
存放内容:    0x78    0x56    0x34    0x12

小端存储格式:
内存地址:    0x4000    0x4001    0x4002    0x4003
存放内容:    0x12    0x34    0x56    0x78
3)4个二进制位等于一个十六进制
十六进制: 4位 0000
2个十六进制: 是一个字节。
因为一个字节=8bit=2位的十六进制。
4)低字节存放在低地址。
大端模式先为高bit位分配空间并存放地址的高bit位。
5)
struct {
    short bit1:4;//假设bit1位a0a1a2a3
    short bit2:9;
    short bit3:3;
};
分析:
a0a1a2a3
------->
高        低
大端模式下: 先为高位分配,也就是从a0开始先分配,
bit1(4位,顺序为a0a1a2a3)    bit2(高4位)    bit2(低5位)    bit3(3位)

5 枚举如何声明?
1) enum weekday{sunday, monday};
其中变量值从0开始。

6 sizeof运算符如何使用?sizeof对函数调用求值的结果是多少?sizeof与strlen的区别是什么?sizeof如何计算数组的大小?
1) sizeof是运算符不是函数,发生在编译时刻,计算操作数的大小。
sizeof(type),返回类型是size_t,实际是unsigned int
2) sizeof对函数调用求值时,返回的结果时函数返回值类型的大小,函数不会被调用。
但函数名称不能计算sizeof值。void类型无法计算。位域成员无法计算。
3)sizeof计算数据所占内存空间,将'\0'计算在内。
strlen不将'\0'计算在内。
C++会给字符串常量末尾自动添加"\0"。
char str[] = "hello";
sizeof(str)=6;
strlen(str)=5;
4)sizeof(数组)=数组的大小
sizeof(指针)=4

7 struct空间计算的规则是什么?请计算如下题目?含有结构体的结构体空间如何计算?
请计算如下含子结构体的题目?请计算含有数组的结构体的题目?
1)原则:
1.1 A)整体空间是占用空间最大的成员所占字节数的整数倍;
B)但若包含子结构体,则是子结构体与父结构体中占用空间最大成员所占字节数的整数倍。
C) 含数组的结构体,数组按照单个变量一一摆放,不是视为整体。
D) 含有位域结构体,
D.1)如果相邻位域字段类型相同,且其位宽之和小于类型的sizeof之和,则后面的字段将紧邻前一个字段存储,
直到不能容纳为止。
D.2) 如果相邻位域字段类型相同,且其位宽之和大于类型的sizeof之和,则后面的字段将从新的存储单元开始,
其偏移量大小为其类型大小的整数倍。
D.3) 若相邻位域字段类型不同,则Dev-C与gcc采取压缩方式。
D.4) 如果位域字段之间穿插非位域字段,则不压缩。
D.5) 整个结构体总大小为最宽基本类型成员大小的整数倍。
E)使用"#pragma pack"结构体空间计算,
#pragma pack(n),编译器将按照n个字节对齐,n为字节对齐数,取值为1,2,4,8,16等,默认为8。
                如果这个值比结构体成员的sizeof值小,那么该成员的偏移量以此 为准,offsetof(item)=min(n, sizeof(item))
                结构体大小是offsetof(item)中最大值的整数倍即可。
#pragma pack(),取消自定义字节对齐方式。
F)空结构体大小为1,用于占位。

1.2 A)数据对齐:按照结构体成员先后顺序排列;
B)但若包含子结构体,当排到子结构体成员时,其前面已经摆放的空间大小必须是该子结构体
中最大类型大小的整数倍,不够则补齐。
2)某计算机存储器按照字节编址,采用小端存放数据,假定int位32位,short位16位,
数据对齐。若程序段如下:
struct{
    int a;
    char b;
    short c;
}record;
record.a = 273;
若record变量的首地址为0xC008,则地址0xC008中内容以及record.c的地址分别是多少?
解:
小端会优先将低位的放在低地址处。
a占据4个字节,char占据1个字节,short占据2个字节,但是因为对齐,导致char和short一共占据4个字节。
----|-|-|--|
a    b    c
a是273等于256 + 16 + 1,对应二进制表示是:
0000 0001 0001 0001
对应十六进制是:
0x 0111
首地址0xC008代表1个字节,存放的是最低位,所以需要十六进制中两位,
所以位0x11。
record.c地址是0xC008加上6个字节,也就是0xC014,即0xC00E
3)
strict S3{
    char c;
    int i;
};
struct S4{
    char c1;
    S3 s;
    char c2;
};
sizeof(S3)=8;
sizeof(S4)=16;
这是因为S3是子结构体,对齐时,会采用子结构体和父结构体中成员中最大成员字节数的整数倍,
因为S3和S4中最大的成员所占字节数是int占据的4字节,
所以c1占据1个字节,s会占据1个字节对齐占据4个字节,并使得c1占据4个字节,i占据4个字节,c2占据1个字节但对齐需要总共4个字节,
所以最终需要4 + 4 + 4 + 4 = 16个字节。
4)
struct s1{
    char a[8];
    int b;
};
则sizeof(s1)=12。
因为数组是按照单个元素计算的。
5)在Linux + gcc环境下,以下结构体的sizeof值是多少?
struct a{
    int f1:3;
    char b;
    char c;
};
分析:
结构体中有位域,需要3位,不足1个字节,占据1个字节,另外两个char分别占据一个字节,
总共3个字节,但是总大小是最宽数据类型int的整数倍,即4的整数倍,所以是4。

8 union空间计算?
整个联合体的sizeof是每个成员sizeof的最大值。

9 enum空间计算?
枚举类型=int=4字节


第四部分 运算符及其优先级


1 前缀运算符与后缀运算符的特点是什么?
前缀运算是先变后用,后缀运算是先用后变。
后置自增只能用于右值表达式。
所以:
(a++) += a;
这句是错误的。

2 ++、--运算符的结合方向是什么?
k=-i++;
因为-与++优先级相同,但是由于自增,自减,符号运算符的结合方向是自右向左,
所以k=-i++=-(i++);

3 有哪些位运算符?
^:按位异或,两位不同为1
~: 取反
<<: 左移
>>: 右移,int右移高位补符号位,unsigned int高位补0

第五部分 C预处理器与内存管理


1 C预处理包含哪些内容?
1)C预处理包含: 宏条文
宏定义与宏替换
条件编译
文件包含

2 如何对常量进行宏定义?如何对带参数的宏定义替换?宏替换的本质是什么?
1) #define 标识符 字符串
2) #define 标识符(参数列表) 字符串
例如: #define FUN(x) ((x)*(x))
注意:如果用宏定义参数,要给每个参数要加括号
3)宏替换的本质是文本替换,不做语法检查,编译前进行,不分配内存。
尽量用const替换宏

3 条件编译的形式有哪些?需要注意什么?如何避免重复包含?
1) 
#if/ifdef/ifndef
#elif
#else
#endif
2)注意重复包含。
3)
#ifndef MA
#define MA
//此处是某个类的定义与相关函数定义
#endif

4 什么是全局变量?如何引用全局变量?如何避免局部变量屏蔽全局变量?
1)全局变量是在函数外部定义的变量,属于一个源程序文件
2)
extern int counter;//引用其他文件定义的全局变量
++counter;
3)使用::或者extern
int counter=3;
int main(){
    exterm int count;
    ......
}

5 static的作用有哪些?类中的static作用是什么?静态数据成员存储在哪里?
静态数据成员为什么不能在类声明中定义?const static成员可以在类的定义提中初始化吗?
静态成员函数与普通成员函数有什么区别?
1)static作用是:隐藏,初始化为0,持久化局部变量的内容,修饰成员函数/变量表示独立于类的对象。
隐藏: 所有未加static前缀的全局变量和函数都有全局可见性
初始化为0:是因为静态变量存储在BSS段,在BSS段中的内存所有字节默认值为0x00
持久化局部变量的内容:静态局部变量生存期为整个源程序。
2)类中的static修饰成员函数/变量表示独立于类的对象。
需要在类的外部进行定义。静态成员属于类,只有一份,被所有对象共享。
3)静态数据成员存储在全局存储区。
4)静态数据成员定义时要分配空间,所以不能在类声明中定义。
样例:
double Student::static_var=1;
5)const static数据成员可以在类的定义中初始化,但必须在类外进行定义。
样例:

6)静态成员函数不与任何对象关联,不具有this指针,无法访问普通数据成员和普通成员函数。
static成员函数不能被声明为const,因为将成员函数声明为const就是承诺不会修改该函数所属的对象。
但是普通函数可以访问静态成员函数与静态成员变量。

6 const作用是什么?const在C与C++中的区别是什么?
1) const把对象转换成常量。
2) C把const认为一个别的地方有存储分配的声明,是外部连接;
C++中const是内部连接
用const代替#define的值替换功能,是因为const有数据类型,const可进行常量折叠来对常量表达式计算求值。

7 指向const的指针和const指针分别是什么意思?const在修饰返回值是起什么作用?
const用来修饰函数参数有什么作用?const在勒种的应用是什么?
1)指向const的指针,样例: const int* cptr;表示是指向const对象的指针,其指向的东西不能修改
const指针表示指针本身是常指针不可修改,需要将const放在*的右边。
样例: 
double d = 1.0;
double const* cptr = &d;
记忆:
const靠近指针,则是常指针;靠近数据类型(例如int)则表示指向const的指针。
2)const修饰返回值,常常用于返回用户自定义的类型。
3)表示函数参数不会改变。
gcc下临时变量都作为常量。
4)const成员函数,例如:
void func2() const;
使得this形参指向的对象为const类型,用于确保该成员函数可以作用于const对象身上。
const数据成员必须在构造函数的成员初始化表中进行初始化。
样例如下:
struct Thing{
    Thing():valueB(1){...}
    int valueA;
    const int valueB;
    static int b;
    const static int c;
};
int Thing::b = 1;
const int Thing:c = 1;

7 堆和栈的区别是什么?C和C++中如何手动申请内存?malloc/free与new/delete的区别是什么?
1)
堆:手动申请与释放,速度慢,自动调用对象的默认构造函数初始化该对象。
栈:编译器自动分配与释放,速度快。
2)
C语言:
char* p1 = (char *)malloc(10);
free(p1);
int *p = (int *)malloc(sizeof(int) * length);
free(p);
C++:
char* p2 = new char[10];
delete []p2;
3)
malloc/free:需要指定分配对象的内存空间大小,无法执行构造函数和析构函数
new:自动计算需要分配空间大小,可用于创建动态对象,调用operator new分配内存,运行该类型的构造函数,
delete:执行对象的析构函数,调用operator delete释放内存。


第六部分 函数 


1C语言中函数传参有哪些类型?C++中函数传参有哪些类型?为什么使用引用传递?编写一个用引用交换两个数?请说出下面的程序结果。
1)C语言中包含值传递和指针传递。
2)C++中是值传递,指针传递和引用传递。
3)引用传递避免实参拷贝,减少空间浪费,对其修改会影响实参本身。
4)
void swap(int &p, int &q){
    int temp = p;
    p = q;
    q = temp;
}
5)
void swap_str(char* a, char *b){
    char* temp = a;
    a = b;
    b = temp;
}
上述程序无法真正交换原来的两个字符串,是因为
只是交换了a,b指针的指向,但是对于原来传入的实参则并内有交换。

2 内联函数作用是什么?
1)内联函数适用于优化只有几行且经常被调用的函数。

3 什么是函数重载?请举例?函数重载中要求参数列表不同具体是指什么?如果仅仅返回类型不同,可以称为函数重载吗?
1)函数名相同但是参数列表不同的函数。
2)
void print(int value){
    cout << value << endl;
}

void print(strin value){
    cout << value << endl;
}
3)参数列表不同具体指参数个数或者参数类型不同。
4)不能。

4 C++程序调用被C编译后的函数,为什么要加extern "C"?
C++为了支持重载会在编译时,对函数名字加入函数返回类型等信息的处理,
但是C语言不会,链接阶段若按照C++函数命名规则取查找C编译的函数就会出错。
告诉编译器该函数是C编译器编译的,用C方式来链接它们。

第七部分 指针与引用


1 指针的算术运算是怎样的?指针+1和-3的含义分别是什么?
1) 指针和整数运算时,会根据指针类型进行调整。
例如:
float + 3 
因为float是4个字节,因此4 * 3 = 12个字节。
2) 指针+1表示指向数组的下一个元素。
指针-3表示指向向左移动3个元素后的元素。

2 typedef的作用是什么?如何使用?
1)typedef用于给已有类型名起别名。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值