精通x语言, 精通c/cpp😂(最后更新于20200629)
static 全局变量vs普通的全局变量
-
static 全局变量只初使化一次,防止在其他文件单元中被引用; static 局部变量和普通局部变量有什么区别: static 局部变量只被初始化一次,下一次依据上一次结果值; static 函数与普通函数有什么区别: static 函数在内存中只有一份,普通函数在每个被调用中维持一份拷贝
-
程序的局部变量存在于(堆栈)中,全局变量存在于(静态区 )中,动态申请数据存在于( 堆)中
完整的说明, 请阅读static作用(修饰函数、局部变量、全局变量)
sizeof vs strlen
- Sizeof operator is a compile time unary operator(单目运算符) which can be used to compute the size of its operand.The result of sizeof is of unsigned integral type which is usually denoted by size_t. sizeof can be applied to any data-type, including primitive types such as integer and floating-point types, pointer types, or compound datatypes such as Structure, union etc.
- strlen(): strlen() is a predefined function in C whose definition is contained in the header file “string.h”.
一个频繁使用的短小函数的实现
- c 用宏定义
- c++用 inline
多维数组的存储
int a[60][250][1000],i,j,k;
for(k=0;k<=1000;k++)
for(j=0;j<250;j++)
for(i=0;i<60;i++)
a[i][j][k]=0;
需要把循环语句内外换一下
macro functions
#include <stdio.h>
#define Square(x) ({ typeof (x) _x = (x); _x * _x; })
#define SQUARE(a)((a)*(a))*
int main() {
int x = 5;
printf("%d\n", Square(x++));
printf("x now is %d!\n", x);
printf("%d\n", SQUARE(x++));
return 0;
}
byte 和十六进制
4个bit(8 4 2 1)可以用1位16进制的数来表示, 所以1个字节byte需用两位16进制数来表示
格式化输出
%5.2s :%5.2s中5表示显示的最小宽度,2表示精度abide只显示前两个
Linux C system 函数
system() executes a command specified in command by calling /bin/sh -c command, and returns after the command has been completed. During execution of the command,
SIGCHLD
will be blocked, andSIGINT
andSIGQUIT
will be ignored.
system fork shell, shell fork出command程序, 需要注意隐式端口占用问题
禁用写文件缓存
setbuf(fp, NULL);
调试时发现日志未被输出可以使用
奇怪的写法(macro)
#if 0 stmt1; #else stmt2; #endif
中stmt1是相当于被注释掉的
-
C++使用new来声明对象时需要使用类指针来接收(new 是在为类申请内存, 所以需要类指针来接收),这一点和C#, JAVA不同,需要注意下, 另外也需要记得用delete来使得析构函数执行。若不需要可直接使用OBJECT obj(1,2,3)这种方式进行对象的声明
-
用typedef 定义一个包含10个整型的类型。
注:typedef int NUMBER[10]; NUMBER 声明为含有10个元素的数组类型。 NUMBER n; n[0] =1; n为含有10个元素的数组。
-
a function macro with no actual body
#ifdef LOGGING_ENABLED #define LOG(x) log_message(x) #else #define LOG(x) #endif
c keyword
-
union(联合):
记得自己当时面试时一道union类型大小的问题做错了😂
-
what is union:
A union is a special data type available in C that allows to store different data types in the same memory location. You can define a union with many members, but only one member can contain a value at any given time. Unions provide an efficient way of using the same memory location for multiple-purpose. c_unions
union Data { int i; float f; char str[20]; } data;
-
联合与结构的区别 “联合”与“结构”有一些相似之处。但两者有本质上的不同。在结构中各成员有各自的内存空间, 一个结构变量的总长度是各成员长度之和(空结构除外,同时不考虑边界调整)。而在“联合”中,各成员共享一段内存空间, 一个联合变量的长度等于各成员中最长的长度。应该说明的是, 这里所谓的共享不是指把多个成员同时装入一个联合变量内, 而是指该联合变量可被赋予任一成员值,但每次只能赋一种值, 赋入新值则冲去旧值。 sizeof(struct) and sizeof(union)
-
cpp keyword
-
auto & decltype(to be contiued):
Type Inference refers to automatic deduction of the data type of an expression in a programming language. Before C++ 11, each data type needs to be explicitly declared at compile time, limiting the values of an expression at runtime but after the new version of C++, many keywords are included which allows a programmer to leave the type deduction to the compiler itself. With type inference capabilities, we can spend less time having to write out things compiler already knows. As all the types are deduced in compiler phase only, the time for compilation increases slightly but it does not affect the run time of the program.
不用显示指定类型, since C++ 11, 这个和C#的var
似乎用法差不多
Error & Debug
-
xxx has initializer but incomplete type / class does not name a type
type was not defined, less of header
-
== 和= 搞错了, if(a=2) , 赋值运算的值为最终的左值
-
errno应该去查输出前所调用的函数是什么, 去看函数的man page
调用一些函数后,最好输出返回值和errno, errno值的具体含义含义可以去搜之前所调用函数或者直接google, /usr/include/errno.h, man errno 等方式了解
错误输出, strerror(errno)
-
进程卡住不动或者CPU占用太高,可能是进入了死循环或者等待事件(阻塞),可以使用
gstack
或者pstack
查看, linux 可以尝试使用sudo strace -p pid
-
使用
gettimeofday
报错, 存储大小未知多半是没有头文件,再或者就是结构体的名字写 -
C++: Cannot declare member function …to have static linkage
暂时只是理解是说头文件里function定义了static后,function实现时不需要加, 需要进一步确认
-
strtok
see man page,strtok(line, search), token = strtok(NULL, search);
在search
被找到的情况下都不会是原本分割前的line
-
string was not declared in this scope
, 哈哈系统应该存在的function或别的什么找不到, 在C++里只能时没有使用相应的namespace
, 如果只是缺失系统头文件, 编译器应当只是给出warning而已
子进程共享父进程SOCKET引发的问题
在调用了bind绑定套接字与地址的server程序中调用system执行command时, 会fork
出 sh -c
, 进而fork
出 command, 这两个程序都也将共享父进程socket的绑定关系, 若command 命令未能执行结束, 而server程序需要再次bind
端口时, 就会报 address already in use 的问题, 问题分析
strcpy时未对NULL 指针做判断导致程序core dump
-
事故背景:
- 某个kafka client在消费消息时采取了处理完再commit的机制, 但处理消息时某一段strcpy 未对NULL指针做判断, 导致消息持续被消费, 程序持续crash
-
strcpy 或其他函数处理NULL指针的方案:
-
项目中,
strcpy
,strnpcy
,printf
,fprintf
,fclose
可以对NULL pointer做判断, 可封装相应函数(注意以下printf
,strcpy
在某些平台, 某些编译器上可能会引发core dump)char *str = NULL; char str2[20] = {0}; printf("str is %s\n", str);//linux, gcc 上未crassh, 输出(null) strcpy(str2, str);//linux, gcc 上crassh printf("str2 is %s\n", str2); fp = fopen("unexist file or no permission", "r"); fclose(fp);//linux, gcc 上crassh
-
malloc, free, new delete
注意: 使用malloc或new申请的内存需要正确使用free或者delete来释放, 在释放后最好将相应指针赋值为NULL,一方面可以在释放前做判断避免double free的问题, 另一方面也避免程序再次访问到野指针中的内容。
今天已经发现了当指针被释放后, 后续声明的指针地址刚好和原有的地址一样的现象,置为NULL就可以避免这种
访问野指针的行为。
int *p = (int *) malloc(sizeof(int) * length);
free(p)
int *p1 = new int;
delete p1;
int *p2 = new int(3);
delete [] p2;
关于指针
from C语言指针详解(经典,非常详细)
也可以阅读读懂复杂C声明的黄金法则
c++ 智能指针这个也可以了解下
复杂指针类型: 说明, 第一次看到有文章系统性的将几乎所有常见的指针类型都分析了一遍, 更关键的是阐明了这个需要借助运算符优先级来分析。
[] | 数组下标 | 数组名[常量表达式] | -> |
---|---|---|---|
() | 圆括号 | (表达式)/函数名(形参表) | -> |
* | 取值运算符 | *指针变量 | <- |
int p; //这是一个普通的整型变量
int *p; //首先从P 处开始,先与*结合,所以说明P 是一个指针,然后再与int 结合,说明指针所指向的内容的类型为int 型.所以P是一个返回整型数据的指针
int p[3]; //首先从P 处开始,先与[]结合,说明P 是一个数组,然后与int 结合,说明数组里的元素是整型的,所以P 是一个由整型数据组成的数组
int *p[3]; //首先从P 处开始,先与[]结合,因为其优先级比*高,所以P 是一个数组,然后再与*结合,说明数组里的元素是指针类型,然后再与int 结合,说明指针所指向的内容的类型是整型的,所以P 是一个由返回整型数据的指针所组成的数组
int (*p)[3]; //首先从P 处开始,先与*结合,说明P 是一个指针然后再与[]结合(与"()"这步可以忽略,只是为了改变优先级),说明指针所指向的内容是一个数组,然后再与int 结合,说明数组里的元素是整型的.所以P 是一个指向由整型数据组成的数组的指针
int **p; //首先从P 开始,先与*结合,说是P 是一个指针,然后再与*结合,说明指针所指向的元素是指针,然后再与int 结合,说明该指针所指向的元素是整型数据.由于二级指针以及更高级的指针极少用在复杂的类型中,所以后面更复杂的类型我们就不考虑多级指针了,最多只考虑一级指针.
int p(int); //从P 处起,先与()结合,说明P 是一个函数,然后进入()里分析,说明该函数有一个整型变量的参数,然后再与外面的int 结合,说明函数的返回值是一个整型数据
int (*p)(int); //从P 处开始,先与指针结合,说明P 是一个指针,然后与()结合,说明指针指向的是一个函数,然后再与()里的int 结合,说明函数有一个int 型的参数,再与最外层的int 结合,说明函数的返回类型是整型,所以P 是一个指向有一个整型参数且返回类型为整型的函数的指针
int *(*p(int))[3]; //可以先跳过,不看这个类型,过于复杂从P 开始,先与()结合,说明P 是一个函数,然后进入()里面,与int 结合,说明函数有一个整型变量参数,然后再与外面的*结合,说明函数返回的是一个指针,,然后到最外面一层,先与[]结合,说明返回的指针指向的是一个数组,然后再与*结合,说明数组里的元素是指针,然后再与int 结合,说明指针指向的内容是整型数据.所以P 是一个参数为一个整数据且返回一个指向由整型指针变量组成的数组的指针变量的函数.
指针基本概念
指针是一个特殊的变量,它里面存储的数值被解释成为内存里的一个地址。要搞清一个指针需要搞清指针的四方面的内容:指针的类型、指针所指向的类型、指针的值或者叫指针所指向的内存区、指针本身所占据的内存区。让我们分别说明。
先声明几个指针放着做例子:
int*ptr;
char*ptr;
int**ptr;
int(*ptr)[3];
int*(*ptr)[4];
指针的类型
从语法的角度看,你只要把指针声明语句里的指针名字去掉,剩下的部分就是这个指针的类型。这是指针本身所具有的类型。让我们看看例一中各个指针的类型:
int*ptr;//指针的类型是int*
char*ptr;//指针的类型是char*
int**ptr;//指针的类型是int**
int(*ptr)[3];//指针的类型是int(*)[3]
int*(*ptr)[4];//指针的类型是int*(*)[4]
复制粘贴很累, 还是直接去原网站看吧C语言指针详解(经典,非常详细)
C,C++的版本
C的版本
c的版本历史, ANSI C(1989) ->= C98/C90(1990) -> C99(1999) -> C11(2011) , 不同版本之间的差异
C++的版本
1998 C++98, 2003 C++03, 2011 C++11, 2014 C++14, 2017 C++17, 2020 C++20 C++ 发展概述
局部会屏蔽全局, 如何使用全局变量?
c++中可以在变量名前加::
(Scope resolution operator), c中怎么办?待考证,不过这种方式代码应该比较容易出错
typedef versus #define in C
-
#define will just copy-paste the definition values at the point of use, while typedef is actual definition of a new type.
-
typedef follows the scope rule which means if a new type is defined in a scope (inside a function), then the new type name will only be visible till the scope is there. In case of #define, when preprocessor encounters #define, it replaces all the occurrences, after that (No scope rule is followed).
// C program to demonstrate importance // of typedef over #define for data types #include <stdio.h> typedefchar * ptr; #define PTR char* intmain() { ptr a, b, c; PTR x, y, z; printf("sizeof a:%u\n", sizeof (a) ); printf("sizeof b:%u\n", sizeof (b) ); printf("sizeof c:%u\n", sizeof (c) ); printf("sizeof x:%u\n", sizeof (x) ); printf("sizeof y:%u\n", sizeof (y) ); printf("sizeof z:%u\n", sizeof (z) ); return0; }
Linux c stack & heap
stack & heap, 经常连在一起说, 但是堆和栈分别是不同的东西。
Stack
栈是一种动态集合,它是一种LIFO(last in first out后进先出)结构, 栈的大小一般都不大, 可以尝试设置或获取stack的大小。getrlimit, setrlimit, prlimit - get/set resource limits
Stack, where automatic variables are stored, along with information that is saved each time a function is called. Four storage classes:auto(mostly used), static, extern, register Storage Classes in C
Heap
堆实际上是以数组形式存储的二叉树
Heap is the segment where dynamic memory allocation usually takes place.
stack overflow: 函数递归调用层次太深(递归条件不对无法返回)或申请的内存数量太多
heap ocerflow: 申请的内存太大或者申请的内存未及时得到释放(memory leak)
why does the storage class of global variable be defined as ‘extern’
回答还没看明白,Why is the storage class of global variables in C implicitly defined as “extern”?
关于字符串常量指针
char *t = "ABCD";
printf("t is %s, %p\n", t, t);
t[0] = 'E';
printf("t is %s, %p\n", t, t);
t是一个指针,直接指向常量区,修改t[0]就是修改常量区的内容,这是不允许的
C关于结构体的初始化和赋值
struct A a;
//错误赋值
a={1,2};
正确的初始化方式, 如下(来源: C语言结构体初始化的三种方法)
// method 1: 按照成员声明的顺序初始化
struct student_st s1 = {'A', 91, "Alan"};
show_student(&s1);
// method 2: 指定初始化,成员顺序可以不定,Linux 内核多采用此方式
struct student_st s2 =
{
.name = "YunYun",
.c = 'B',
.score = 92,
};
show_student(&s2);
// method 3: 指定初始化,成员顺序可以不定
struct student_st s3 =
{
c: 'C',
score: 93,
name: "Wood",
}
struct student_st stus[2] =
{
{
.c = 'D',
.score = 94,
/*也可以只初始化部分成员*/
},
{
.c = 'D',
.score = 94,
.name = "Xxx"
},
};
更多方式及解释, 请阅读C/C++结构体初始化与赋值
assert(c/c++)
void assert(int expression);
Readlist
- undeclared vs undefined: 声明和定义, 变量的声明、定义、赋值和初始化有什么区别?, C语言中声明和定义详解
- C++中指针和引用的区别
- 思考为什么实际睡眠时间比设想要尝一些的原因(程序未能及时从睡眠状态过度到就绪状态后未能获取到时间片), sleep,nanosleep以及clock_nanosleep函数
- 重入,线程安全,异步信号安全,中断安全和取消安全, reentrancy, thread safe, asynchronous signal safe, interrupt safe and cancellation safe,注意可重入(一次执行未完成前再次调用)和幂等(幂等矩阵,idempotent matrix, AA=A)不同, 业务上的解释可阅读谈谈web系统中的可重入,幂等性,分布式事务的那些事
- 关于C++ const 的全面总结
- Returning C string from a function
- 对内存重叠的深入认识
- Linux-静态链接库和动态链接库
- C语言在子函数中调用malloc申请内存的方法
- C++ 值传递、指针传递、引用传递详解
- c/c++ max/min 4种实现方法
- Linux下可执行文件格式详解, 读完这个可以解决自己的疑问为什么声明了1个很大的全局数组编译后,可执行程序文件的大小没有发生太多变化
- C++程序调用C函数, extern “C”的作用详解
- Use of %d inside printf to print float value gives unexpected output, Undefined Behavior in C
- 函数内局部变量一定要初始化,否则可能取到“旧值”, 函数调用的栈指针移动(一个诡异的重发消息的问题,最后boss出马替自己解决了一个6个月的bug😂) C 程序对栈空间的回收都不屑一顾,因为它根本不回收,而是旧的数据会被新的数据覆盖
- 数组的首地址是常量! 数组、数组名、数组地址学习
- C没有动态数组, C++才有, int array[n];
- C++ 闭包的理解
- linux环境下操作特大文件:32位程序
- TCP状态转换图总结
- How to do an atomic increment and fetch in C?