数据类型和头文件
基本数据类型
数值型(short,int,long,long long ,float,double)
//位运算将char,unsigned char,short等自动提升到int或unsigned int进行位运算(称为整形提升) java c++都会进行整形提升
//例如 short a=10; short b=11; short c=a+b;当执行c=a+b时,a和b都会转成int,计算后转为short给c
char|str 字符串(结尾为\0)
char name[]="me33";
char name2[5]="me";//为 m e \0 \0 \0 即其他被填为\0
strlen(name) //返回4,只返回可见字符长度
数组
int data[2]={1,2}; // int data[2][3];
数组名就是数组第一个元素的地址;
特殊数据类型
枚举
enum color{red,blue,green,white,black}; //本质是int,例如blue=1
enum color c1=blue; if(c1==blue){}//赋值,判断
指针
指针+n:是取下n个存储单元的地址;指针-n;指针比较
&var //获取var的地址
int *pData; //申明指向int数据类型的指针变量
*pInt //获取pInt对应的变量,放表达式左边为赋值,放表达式右边为获取值
结合优先级:括号() > 数组[]>指针* > 其他数据类型 参考下方代码块
看一个指针的类型,直接把指针的名字去掉,剩下的就是指针的类型。参考下方代码块
要知道指针所指向的类型,把指针的名字和*去掉,剩下的就是指针所指向的类型。注意优先级。参考下方代码块
参考链接: https://www.cnblogs.com/mokongking/p/17329296.html
结构体(结构名只是结构体的名称,&才能获取其地址)
struct Student{ char name[100]; int age;}; //结构体定义
struct Student s1;//声明结构体变量
printf("%s",s1.name); s1.name="cj"; //结构体的使用 或者 struct Student s2={"cj",12};进行初始化
结构体指针
struct Student *sp=&s1;
使用: (*bookp).name bookp->name 两种方式
传递结构体或者结构体指针给函数,或者作为函数返回值,可以用const修饰来保护原始数据;且只是传递结构体的副本而已;当然可以传递结构体的地址给函数
宏
1.#define 宏 值 //数字常量,字符串常量,表达式等;一旦找到宏,会被值替代;
#define SQUARE(X) (X)*(X) 可以为类函数宏
//函数宏用括号包含,避免产生副作用
#define DllExport __declspec( dllexport ) //宏定义,定义导出的函数或类
class DllExport CameraSp{} //导出CameraSp类
int DllExport addTwo(int a,int b)//导出函数
2.#include "" // #include <>
<>用于系统头文件,一般到保存系统标准头文件的位置查找头文件
“”用于自定义的头文件,先在当前目录查找头文件,然后去系统头文件目录查找
cpp文件引用c编写的头文件时,需要extern "C" { #include <libavformat/avformat.h> } 否则可能找不到符号
3.条件编译
#ifndef,#ifdef,#else,#endif
4.typedef
typedef long long JLong;
typedef const double *(*fp)(const double *,int) //将fp简化,fp是个指向函数的指针,是个类型名称;把fp拉到最后容易理解
typedef提供别名而已
#ifdef MAVIS
#include "s1.h"
#define COUNT 10
#else
#include "s2.h"
#define COUNT 20
#endif
如何保证头文件中的标识符唯一:
例如xx.h
#ifndef XX_H
define _XX_H
.....
#endif
int p; // p是一个普通的int型变量
int *p; // p先与*结合,后与类型结合,所以p是一个指针,指针指向的内容是int型
int p[3]; // p先与[]结合,然后与类型结合,说明p是一个数组,数组里面的内容是int型
int *p[3]; // p先与[]结合,说明p是一个数组,然后再与*结合,说明数组p里面存放的是指针,然后与类型结合,说明指针指向的内容是int型,p是一个由返回整型数据的指针所组成的数组
int (*p)[3]; // ()的优先级最高,(*p)说明p是一个指针,然后与数组[3]结合,说明指针指向的是大小为3的一个数组,然后与类型结合,说明指针所指向的数组里面的内容为int型,p是一个指向由整型数据组成的数组的指针
int **p; // p先和最近的*结合,说明p是一个指针,然后再与剩下的*结合,说明指针p指向的是一个指针,然后与int结合,说明指针p所指向的指针(*p)所指向的内容(**p)为int型
int p(int); // p先于(int)结合,说明这是一个函数,函数的参数为int型,然后与外面的int结合,说明函数返回的内容是int型
int (*p)(int); // ()的优先级最高,(*p)说明p是一个指针,然后与(int)结合,说明指针所指向的内容是函数,函数的参数为int型,然后与外面的int结合,说明指针p所指向的内容(*p)是一个函数,该函数的返回内容为int型
int *(*p(int))[3]; // 一层一层的剥开,p首先与(int)结合,说明p是一个函数(这里说了p是一个函数),函数的参数为int型,然后与*结合,说明函数返回的是一个指针,然后与数组[3]结合,说明是返回的指针指向的是一个数组,然后与*结合,说明数组里面的内容是指针,然后与外面的int结合,说明数组里面的指针指向的内容是int型。所以p是一个参数为int型并且返回的是一个指向内容为int*型的数组的函数,由于int*是指针,指针指向的内容为int型,所以p返回的就是一个指向内容为指向内容为int型的指针的数组的函数(太绕了,注意段句)
int *(*p(int))[3]; // 这个过于复杂,再来一遍
// 括号里面的内容是一个整体,直接把(*p(int))替换为ptr,那么表达式就变成了
int *ptr[3]; // 这就简单多了,通过上面的例子可以知道,ptr是一个数组,数组里面的内容为指针,指针指向的内容为int型,即ptr是一个内容为指向内容为int型的数组
*p(int); // 同样通过上面的例子可以知道p是一个函数,函数的参数为int型,函数的返回内容是指针
// 下面就把他们结合起来
int *(*p(int))[3]; // p是一个函数,函数的参数为int型,函数的返回内容是一个指针,该指针指向的内容是一个数组,数组里面的内容为指针(该指针指向的内容为int型)
int p; // 去掉p,p是一个普通的int型变量
int *p; // 去掉p,说明指针的类型是int *型
int p[3]; // 去掉,说明p的类型为int [3],是一个普通的数组
int *p[3]; // 去掉p,说明指针的类型是int *[3]型
int (*p)[3]; // 去掉p,说明指针的类型是int (*)[3]型
int **p; // 去掉p,说明指针的类型是int **型
int p(int); // 去掉p,剩下int (int),是一个普通的函数
int (*p)(int); // 去掉p,说明指针的类型是int (*)(int)型
int *(*p(int))[3]; // 去掉p,说明指针的类型是int *(*(int))[3]型
int *p; // 去掉p和*,说明指针所指向的类型是int型
int *p[3]; // 因为[]的优先级高,所以p先和[3]结合,说明p是一个数组,再与*结合,说明这是一个指针数组,所以这里首先将p[3]去掉,然后再去掉*,剩下int,所以指针数组p[3]里面的元素所指向的内容为int型
int (*p)[3]; // 去掉p和*,说明指针所指向的类型是int [3]型,所以指针p指向的是大小为3的int型数组
int **p; // 去掉p和*,说明指针所指向的类型是int *型
int (*p)(int); // 去掉p和*,说明指针所指向的类型是int (int)型,所以指针p指向的是参数为int的函数,函数的返回类型为int型
int *(*p(int))[3]; // 因为()优先级高,p和(int)结合,说明p是一个函数,然后与*结合,说明函数的返回类型是一个指针,所以这里应该直接把(*p(int))去掉,剩下int *[3],所以函数p返回的是一个指向int *[3]型的指针
函数
结构 back_type func_name(params){}
变量考量:作用域,链接性,存储期
计算机内存分区
堆(malloc或new申请)
栈(存储局部变量和函数调用信息。局部变量包括基本数据类型和指向其他数据结构的指针)
全局(静态)存储区:用于存储全局变量和静态变量
代码区:存放程序的二进制代码;在程序运行期间不会改变
作用域:块,文件,静态变量作用域取决于声明位置
存储期:自动(函数),静态(程序结束,例如全局变量),动态(堆中,new->delete或程序结束后消失)
链接性:内部(全局变量+static),外部(全局变量),无(代码块+static)
static修饰作用:将变量放入全局存储区,并且限制在文件内部,外部文件无法访问或引用
全局变量和用static修饰的静态全局变量区别在于外部是否可以访问
一个文件声明了全局变量,另一个文件可以用extern来使用这个全局变量(但是不能赋值)extern int count;
extern 可用于函数,为默认形式;可以用extern "C" func(); 声明原型,表示采取C语言链接性
函数指针
函数名词即为函数的内存地址
例如 double sum(int x) 该函数要作为参数传入其他函数,则为 double (*fp)(int)
申明一个函数double *mm(double *,int); 如果在函数中定义一个该函数类型的变量pf则为
double *( *pf)(double *,int)=mm;
编译
头文件
包含内容
函数原型
#define或const定义的常量
结构声明
类声明
模板声明
内联函数
在一个文件中,同一个头文件只能包含一次(因此使用之前的防护方案#ifndef:此方案仍包含了多次,但是会忽略此间内容)
编译选项(gcc,g++都可以使用)
预处理,编译部分:
-E 预处理后停止,主要是对宏的处理 例如 gcc -E a1.c -o a1.i
-S 进行编译生成汇编代码 例如 gcc -S a1.i -o a1.s
-c 将汇编代码生成目标文件(机器码),不可执行,因为还没进行连接(把库代码,其他文件代码引入) 例如 gcc -c a1.s -o a1.o
最后 gcc a1.o -o a1 进行连接生成可执行文件;当然也可以一步到位,gcc a1.c -o a1生成可执行代码
多个文件进行编译连接: gcc a1.c a2.c a3.c -o aa
注意:如果进行编译连接,多个文件只能有一个main函数;但是只是生成目标代码,则不必有main函数
-Wall:给出警告信息
链接部分:
-Idirectory 向gcc的头文件搜索路径下添加新目录
-L libdir 向gcc的库文件搜索路径下添加新目录;如果使用了自定义目录,该链接信息会被包含在可执行文件中,默认为/usr/lib下
-l name 提示连接程序在创建可执行文件的时候包含指定的库文件
链接静态库(libname.a)或动态库(libname.so)的库文件
ar指令
常以.a,.lib(windows)结尾
ar rcs libx.a file1.c file2.c (或者rc?) --》生成静态库
-shared 动态链接库;默认情况下,gcc会进行动态链接库
常以.so,.dll(windows)结尾->用于动态链接(-shared)
gcc -shared -fpic -o libx.so file1.c file2.c ---》生成动态库
方式2:gcc -fpic -c file1.c -o file1.o 然后 gcc -shared file1.o fil2.o -o libx.so //so可以变成libx.dll
生成可执行文件
-static 静态链接所有库,创建一个静态可执行文件。静态链接优先。
多个动态库的时候,顺序从高层次到低层次
gcc main.c ./libmymath.so -I inc/
gcc main.c -lmymath -I inc/ //这种情况下动态库在系统路径下自动搜索 libmymath.so库
gcc main.c -lmymath -L/usr/lib/ -I inc/ //这种情况下动态库在系统路径下自动搜索 libmymath.so库
gcc main.c -Wl,-rpath=./lib -lmymath -I inc/ //指定运行时搜索库的路径