C语言进阶
32位系统
char 1
short 2
int 4
long 4
float 4 无符号
double 8 无符号
const * 左定值
* const 右定项
变量
表达式
--,++只能用于变量
---
sizeof 返回无符号
有符号和无符号运算,首先自动转成无符号
int m=2,n=3;
int k = m+++n;//先用m再自加
m=3,n=3,k=5;
int m=5;
int n=3;
int k=m+++-+-+--n;
m=6,n=2,k=(m++)(+)(-)(+)(-)(+)(--n)=7
宏
1.常量替换
#define MAX_CLASS 10
2.类型定义
#define INTEGER int
3.宏函数(参数需要用()括起来)
容易引发优先级低的问题
#define MAX(x,y) (((x)>(y))?(x):(y))
4.防止头文件重复包含
#ifndef TEST_H
#define TEST_H
#include "xxx.h"
#ifdef _cplusplus
//下面的代码是用c语言进行声明和定义的
//如果不加,那么头文件的编译方式由include此头文件的源文件决定
extern "C"{
#endif
//头文件内容
#ifdef _cplusplus
}
#endif
#endif
5. 内定调试宏
_LINE_:当前代码行数
_FILE_:当前源文件名
_DATE_:当前日期
_TIME_:当前时间
宏风险
1. 容易引发优先级低的问题
参数需要用()括起来
2.多次运算
参数为函数的时候
3.同一个宏名字多处定义
4.宏空格
#define func (x) (x+1)
宏函数运行顺序
#define PT = 3.5;
#define S(x) = PT * x * x;
S(1+2) = 3.5*1+2*1+2=7.5//这才是要加括号的原因
枚举
枚举的大小和编译器,编译选项有关
建议:
1. 不要使用枚举的大小(与编译器有关)
2. 枚举的值不要超过32位
用法
用于声明一组常数。当一个变量有几个固定的可能取值时,可以将这个变量定义为枚举类型
enum Season {spring, summer, autumn, winter} s;
s = spring; // 等价于 s = 0;
s = 3; // 等价于 s = winter;
结构体
结构体对齐
1. 运行速度更快
2. 指针从基地址读取数据可能出错
3. 结构体直接赋值可能会出错
结构体大小
//20
typedef struct{
int (*a[5])();
}Foo1;
//8
typedef struct{
int (*a)[5];
char b;
}Foo2;
//20
typedef struct{
int a[5];
}Foo3;
//20
typedef struct{
int *a[5];
}Foo4;
typedef struct{
u8 a;
u8 b;
u8 c;
}TEST;
TEST = test{1,2,3};
u16 usShort;
u32 ulInt;
usShort = *(U16*)&test.a;
usShort = *(U16*)&test.b;
ulInt = *(U32*)&test.c;
是否出错和cpu以及编译器相关
解决方法
1.自然对齐
(1)成员按照8,4,2,1顺序排列
(2)不够对齐的字段用保留字段填充
2.预编译对齐 #pragma pack(n)
3.不对齐结构不用非同类型指针访问,不进行类型转换
4.尽量不对结构体进行直接赋值,使用memcpy拷贝整个结构体
如何对齐
1.以自身最大的为自身对齐值
2.自身对齐值与指定对齐值中的小值
注意:
1.0长度的数组不占用空间
b[0],b[]:直接通过b访问后面结构体内存
联合体
同一个内存空间存储不同的数据类型
小端模式
高字节在高地址,低字节在低地址
联合体赋值时,短数据赋值之后,长数据的空余位数据未知
基本用法
typedef union
{
unisigned int a;
unisigned int b;
}TEST_U1;
当u1.a = 0x12345678;
u1.b = 0x12345678;
typedef struct
{
unisigned char a;
unisigned char b;
unisigned char c;
unisigned char d;
}TEST_S3;
typedef union
{
unisigned int a;
unisigned char b;
unisigned short c;
}TEST_U2;
当 u2.a = 0x12345678
u2.b = 0x78;
u2.c = 0x5678;
typedef union
{
unisigned int a;
TEST_S3 b;
}TEST_U3;
当 u3.a = 0x12345678;
u3.b.a = 0x78;
u3.b.d = 0x12;
联合体大小
最大字段的大小
函数
函数定义声明
int Tesy1();
void Test(int num,char array[]);
函数指针
typedef int (*PFUN)();
PFEUN pf = Test1;
函数入参
1 4 4 4
全是指针,数组的定义其本质都是指针
inline函数
目的
a)没有调用开销,效率高
b)是真正的函数,编译器会监测参数类型,消除宏函数隐患
c)太复杂或调用点太多,展开后会导致代码膨胀带来的恶化可能大于效率提升带来的益处
a)只是对编译器的建议,编译器可以忽略
b)在调用内联函数时,要保证内联函数的定义让编译器看到,即在头文件中定义内联函数,这与通常的函数定义不一样
static函数
static int Test1();
目的
a)限定作用域,只能被本文件中其他函数调用,不能被同一程序其他文件中的函数调用
extern函数
extern int Test1();
目的
a)函数和变量前,表示变量或函数的定义在其他文件中,
提示编译器遇到此变量和函数字其他模块中寻找定义
b)取代include "*.h"来声明函数
c)extern "C"
C++在编译时为解决函数多态问题,会讲函数名和参数联合起来生成一个中间的函数名,
而c语言不会,因此会导致链接时找不到对应函数的情况。
链接: link.
数组与指针
指针
char ch = 'c';
char *pch = &ch;
char *pStr = "abc";//pStr[1] = b (*pStr+2)
int m;
int *pm = &m;
struct Test stTest;
struct Test *pst = &stTest;
//函数指针
extern void TestFunc(int m);
void (*pf)(int) = TestFunc;
int *(*a[5])(int, char*);
=
typedef int* (*f)(int,char*);
f a[5];
a是个5个元素的数组,每个元素为函数指针.
函数指针
#include <stdio.h>
#include <stdlib.h>
int* (*a[5])(int,char*);
int *foo(int n, char *s)
{
int *p;
p = (int *)malloc(sizeof(int));
*p = n + atoi(s);
return p;
}
int main(int argc, char *argv[])
{
int *p;
a[0] = &foo;
p = (*a[0])(1, "2");
printf("%d\n", *p);//3
return 0;
}
链接: link.
指针的大小
int ll[6];
//注意*ll[3] = ll[3];int数组
//数组指针
int (*l)[3]=≪
int p[5];
printf("%d\n",sizeof(l));
printf("%d\n",sizeof(*l));
printf("%d\n",sizeof(p));
return 0;
结果
指针数组的首地址长度是4;
指针数组取地址是指针数组的大小12;
数组则是指定的大小20;
指针类型转换
int a[5] = {1,3,5,7,9};
char *p = a;
int *t = (int *)p;
printf("%d %d %d %d",*p,*(p+sizeof(int)),*(++t));
结果
不可
((int*)p)++;
在 C 语言中, 类型转换意味着 ``把这些二进制位看作另一种类型, 并作相应的对待"; 这是一个转换操作符, 根据定义它只能生成一个右值 (rvalue)。而右值既不能赋值, 也不能用 ++ 自增。(如果编译器支持这样的扩展, 那要么是一个错误, 要么是有意作出的非标准扩展。)
数组
char m[10];
char m[] = "123";
char m[] = {'1','2'};
int m[3][2];
int *k[10];
//自带了/0所以是18+1
char ac[] = "welcome\0to\0huawei\0";
//strlen到\0结束为7
printf("%d %d\n",sizeof(ac),strlen(ac));
结果
int c[4][2];//0x100;
(c+1) = 0x108;//步长为2 int 8
(*(c+1)+1) = 0x10c //8 + 4
声明一个函数,返回二维数组,int,最右空间为10.
int (*Func())[10];
数组为参数时,下面的都是一样的
char p[10];
char p[];
char *p;
char p[10][2];
char (*p)[2];
char p[][2];
char test_5[] = "123";
sizeof(test_5) = 4//包含'\0'
内存分布
printf(%s\r\n,dataList);
1. 字符数组局部变量,返回后变量内存收回 。\?
2. 字符数组局部变量,返回后变量内存收回。 \?
3. 字符串常量,全局存在。abc
4. 静态变量,作用域只在函数内,空间和全局变量一致。abc
int a = 0x0506;//全局变量初始化,数据段
char *p0 = "this is test string";;//全局变量初始化,数据段
char *p1;//全局变量未初始化 bss段
int main()
{
iny b;//栈
char s[] = "abc";//栈
char *p2;;//栈
char *p3 = "123";//123在常量区,p3在栈上
static int c =0x0203;//全局(静态)初始化区
static char *str1 = "abcd";//全局(静态)初始化区
p2 = (char*)malloc(10);//堆
strcpy(p2,"5678");
free(p2);
return 0;
}
字符串在数据段还是在代码段由编译器决定
int gy;//初始化为0
void Foo(){
int x;//未知
int *p;
for(int i;i<10;i++){
p = &i;//出了这个大括号i无效化
}
printf("%d %d %d %d",gy,x,*p,i);//报错
return;
}
BSS段:存放程序中未初始化的全局变量,不占用执行程序大小,其内容由操作系统初始化(清零)。
数据段:存放程序中已初始化的全局变量。
代码段:存放程序执行代码的内存区域,大小在程序运行前已经确定,并且通常属于只读。
堆:存放进程运行中被动态分配的内存段,大小并不固定,可动态扩张或缩减,马路咯从分配的为此内存。
栈:用户存放程序临时创建的局部变量(不包括static声明的变量)。在函数被调用时,其参数也会被压入发起调用的进程栈。
void Test(){
static char *str = "abc";
}