关于C语言的一些总结(菜鸟版本)
typedef unsigned int uint16;
define num 8
数组int a[10] typedef struct ab {uint16 a; uint16 b; uint16 c;}abb; abb stu[10]; void fun() ; 其中数组的名字a 和stu 函数名fun代表首地址,
但是,如果是单独的结构体,abb stu; stu并不代表首地址,首地址要用到&符号
a+3 代表第三元素的地址 a[3]代表第三个元素 同理 stu[1]不是代表地址,而是第二个结构体本身,可以这样复制stu[1].a=2;
stu+1才代表第二个结构体的地址
与&stu[1]是一个意思
printf
字符 | 对应数据类型 | 含义 |
d / i | int | 接受整数值并将它表示为有符号的十进制整数,i是老式写法 |
o | unsigned int | 无符号8进制整数 |
u
|
unsigned int | 无符号10进制整数 |
x / X
|
unsigned int | 无符号16进制整数,x对应的是abcdef,X对应的是ABCDEF |
f
|
float或double | 单精度浮点数或双精度浮点数 |
e / E
|
double | 科学计数法表示的数,此处"e"的大小写代表在输出时用的“e”的大小写 |
g / G
|
double | 使用以上两种中最短的形式,大小写的使用同%e和%E |
c
|
char | 字符型。可以把输入的数字按照ASCII码相应转换为对应的字符 |
s / S
|
char * / wchar_t * | 字符串。输出字符串中的字符直至字符串中的空字符(字符串以'\0‘结尾,这个'\0'即空字符) |
p
|
void * | 以16进制形式输出指针 |
n | int * | 到此字符之前为止,一共输出的字符个数,不输出文本 |
%
|
无输入 | 不进行转换,输出字符‘%’(百分号)本身 |
int main(int argc, char ** argv) int main(int argc,char * argv[])
int a=10 b=20 c=30
printf("%d%d%d",a+b+c,b=b*2,c=c*2) ; 结果 110 40 60 因为函数的参数默认是从右到左处理的。
char *p p是指针变量,其值是可以修改的,但是不能通过P修改指向的字符串
char p【】 p是数组变量,其值不可以修改,但是可以修改p指向的字符串
连接符 #define XXX(a,b) a##b //## 是连接符
printf(“%d\n”,XXX(fun,2)(100));
等价于 fun2(100);
地址空间:为了比较直观的知道地址空间的大小,可记以下规律。4K --> 0x00001000,
64K --> 0x00010000,
1M --> 0x00100000,
16M --> 0x01000000,
256M--> 0x10000000,
3个0 是4K 4个0 是64K 5个0 是1M 6个0 是16M 7个0 是256M
为什么3个0表示的空间大小是4K小呢? 地址从0x00000000起始,到0x00001000其间则有16*16*16个地址,每一个地址表示1bit空间大小。 那其空间大小是:(16*16*16)/1024= 4 k
每个地址,如0x10000000单个地址,它是16进制,所以每一个数值需要4个bit来表示(2的4次方=16), 所以其占用空间32bit。
为什么bit成为了最小数据单元?(以下为个人的理解) 电子电路它只有0,1两种信号(再傻瓜式的说明就是只有通电或断电),所以我们定义了能标志0或1的单元为bit。然后我们开始采用顺序堆积很多这样单元,就能为我们记录很多标识(称作数据或信息)。
但是是谁在控制这个16进制的地址呢,是CPU吗?或说是这么多16进制的数值(编号)它又是保存在那呢?每个bit空间,是不可能存放一个32位的16进制的,就算可以也没有意义。那CPU它又是根据什么来进行寻址呢?它是如何做到可以直接跳转读取到指定的地址?
2.
名称 | 标识符 | 大小 | 表示范围 |
短整型 | shortint | 1字节 | -128~127 |
整型 | integer | 2字节 | -32768~32767(-2^15~2^15-1) |
长整型 | longint | 4字节 | (-2^31~2^31-1) |
64位长整形 | int64 | 8字节 | -2^63~2^63-1 |
字节型 | byte | 1字节 | 0~255 |
字型 | word | 2字节 | 0~65536 |
双字型 | dword | 4字节 | |
四字型 | qword | 8字节 | 0~2^64-1 |
1.typdef void (*funcptr)(void);的解释说明( 函数指针) 原型初见于stm32的IAP bootloader编程“跳转地址函数”
定义一个函数指针类型。
比如你有三个函数:
void hello(void) { printf("你好!"); }
void bye(void) { printf("再见!"); }
void ok(void) { printf("好的!"); }
typdef void (*funcptr)(void);
这样就构造了一个通用的函数
你用的时候可以这样:
void speak(int id)
{
funcptr words[3] = {&hello, &bye, &ok};
funcptr fun = words[id];
(*fun)();
}
这样的话,如果speak(0)就会显示“你好!”
speak(1)就会显示“再见!”
speak(2)就会显示“好的!”
用于处理参数和返回值的形式都一样,但是功能不确定的一组函数,可以使用函数指针。
比如算术运算符,加、减、乘、除,都可以用typedef int (*calc)(int,int)代表,等、
2.(指针和形参传递)
1. 零值比较
一般意义上的零值有布尔型,整型,指针,浮点型。
(1) 布尔型:C语言里面没有布尔类型,但是C99标准提供了一个库来表示,头文件为<stdbool.h>,其表示为bool mybool = true;注意都是小写,其实在C语言windows里面也定义了一个BOOL来区别于C++的bool。
1) #include <stdbool.h>
2)
3) int main(void)
4) {
5) bool mybool = true;
6) if(mybool)
7) printf("mybool is true\n");
8) else
9) printf("mybool is false\n");
10) printf("c bool size is %d\n", sizeof(mybool));
11) return 0;
12) }
结果为:
mybool is true
c bool size is 4
注意和C++的布尔型的区别,C语言里面的布尔型是用整数来代替的,零是假,任何非零的值为真,C++的布尔型是一个一个字节的变量。
1) //#include <stdbool.h>
2) #include <iostream>
3) using namespace std;
4) int main(void)
5) {
6) bool mybool = true;
7) if(mybool)
8) cout<<"mybool is true"<<endl;
9) else
10) cout<<"mybool is false"<<endl;
11) cout<<"c++ bool size is "<<sizeof(mybool)<<endl;
12) return 0;
13) }
结果为:
mybool is true
c++ bool size is 1
(2) 整型:同类bool型用法一样 int test = 0;
if(test){
}
else{
}
(3) 指针:指针的零值为空指针NULL, int *ptest = NULL;
if(test == NULL){
}
else{
}
(4) 浮点型:浮点型的数据与零值比较不是直接测试与零是否相等,而是测试是否在0值左右的一个范围内。
if(fabs(flag2) < 1e-5)
printf("flag2 is 0\n");
else
printf("flsg2 is not 0\n");
2. 函数形参传递
(1)Strcpy函数的实现:
char *mystrcpy(char *des, const char *sur)
{
char *p = des;
assert((des !=NULL ) && (sur != NULL));
while((*des++=*sur++) != '\0');
return p;
}
(2)示例1:
1) void getmemory1(char *p)
2) {
3) p = (char *)malloc(100);
4) }
5) void test1(void)
6) {
7) char *str = NULL;
8) getmemory1(str);
9) strcpy(str, "hello world1 \n");
10) printf(str);
11) }
由于传入函数getmemory1的参数为指针,编译器为其准备一个副本_p传入,存在在系统栈里面,我们在getmemory1函数里面,为其动态分配空间存在于堆上,但是里面使用的是p的副本,分配空间的头指针指向的是_p,而不是p,所有该函数结束时p的值仍然没有改变,这里函数结束后不会把分配的空间释放掉,因为其分配的空间在堆上,如果程序员不释放的话其空间要在程序结束后由操作系统收回。测试函数使用未改变的的指针,其值为NULL,所有会出错。
(3)示例2:
1) char* getmemory2(void)
2) {
3) char p[] = "hello world2 \n";//在栈上
4) return p;
5) }
6) void test2(void)
7) {
8) char *str = NULL;
9) str = getmemory2();
10) printf(str);
11) }
由于函数getmemory2在内部使用了char p[] = "hello world2 \n"; 所有我们这里说一下他和
char *p1 = "hello world2 \n";的区别,首先char p[] = "hello world2 \n"都是存放在栈上的,函数执行完会自动释放,而char *p1 = "hello world2 \n";却不一样,char *p1存放在栈上这与前面一样,但是"hello world2 \n"却是存放在常量区的,程序结束后由系统释放 ,而且char p[]是一个数组名,为常量,而char *p1不是,在getmemory2函数中,char p[] = "hello world2 \n"都是存放在栈上的,函数作用范围结束后会自动释放,所以,即使返回了p指针,我们打印出来的字符串也是乱码但是如果将函数内部char p[] = "hello world2 \n"改变为char *p1 = "hello world2 \n",则不会出错,程序代码如下:
1) char* getmemory2(void)
2) {
3) char *p1= "hello world2 \n";
4) return p1;
5) }
6) void test2(void)
7) {
8) char *str = NULL;
9) str = getmemory2();
10) printf(str);
11) }
从这里我们可以认为,char p[] = "hello world2 \n"是一个字符型数组,p为数组名不可以改变,而char *p1 = "hello world2 \n"是指针指向的字符串常量,p1为可变指针,而字符串常量存在常量区。
(4)外传:字符串常量,我们可以知道"hello world2 \n"是一个字符串常量,并将第一个值的位置给一个指针p1,我们来看一下下面的表达式:
"hello world2 \n"+1
其实我咋一看,感觉这表达式没什么意义,在看了C和指针才知道,其实这个也是很有意思的,
"hello world2 \n"+1指向的是字符串常量的第二个字符,这样我们干脆就把它当成一个指针来看,这样下面的表达式也就有了意义
*"hello world2 \n"
这个表达式很明显的意思就是取字符串常量的第一个字符,依次我们可以进行的运算就可以知道如
"hello world2 \n"[3]; //等效于*("hello world2 \n"+3)
注意:由于字符串常量位于常量区,他的生命周期是恒定不变的,而且由于是常量,它不允许改变它的值,是只读的。
(5)示例3:
1) void getmemory3(char **p, int num)
2) {
3) *p = (char *)malloc(num);
4) }
5) void test3(void)
6) {
7) char *str = NULL;
8) int num = 100;
9) getmemory3(&str,num);
10) printf(str);
11) }
这次这个示例可能有点难懂了,传入函数getmemory3可以说是指针的指针,假设这里编译器为其准备的副本为__p,那么*__p即是指向char类型的指针,那么我们可以像平常那样对其动态分配空间,你可能会有疑问,为什么这里可以为他动态分配呢,虽然编译值指定__p = p;
但是他们指向的地址都是同一个,*__p = *p,分配空间后的头指针付给*__P 相当于赋给了*p,但是最好不要用这个方法分配空间。
注意:释放分配的空间后将指针设置为NULL,避免形成野指针。
(6)示例4:
1) char* getmemory3(void)
2) {
3) char *p = (char *)malloc(100);
4) printf("len is %d \n",strlen(mystrcpy(p, "hello mywold3")));
5) return p;
6) }
7) void test3(void)
8) {
9) char *str = NULL;
10) str = getmemory3();
11) printf(str);
12) }
getmemory3函数里面,char *p = (char *)malloc(100);分配空间,虽然指针p存在于系统栈在函数结束后将消亡,但是分配的空间存在于堆不会,返回分配的首指针,使该动态分配的区域有指针指向,置于可用状态。
3. 指针
我们这里直接通过下面一段代码来讲解:
char ch[7]="13579";
char cr = 'm';
char *cp = &cr;
printf(" &cr is %0x\n", &cr);//取cr变量的地址,也就是’m’存储的位置
printf(" cp is %0x\n", cp);/变量赋值的时候就是为’m’存储的位置
printf(" &cp is %0x\n", &cp);//取cp的地址
printf(" *cp is %c\n", *cp);//从存储’m’的位置,取出m
cp = ch;
printf(" *cp+1 is %c\n", *cp+1);//从存储’m’的位置,取出m并将m的值加‘*’优先级高
printf(" *(cp+1) is %c\n", *(cp+1));//地址cp加单位字节,然后取出+1地址的内容
printf(" cp is %0x\n", cp)
printf(" ++cp is %0x\n", ++cp);//地址cp先加单位字节再使用
printf(" cp is %0x\n", cp);
printf(" cp++ is %0x\n", cp++);//地址cp先使用再加单位字节
printf(" *cp is %c\n", *cp);
printf(" *++cp is %c\n", *++cp);//*,++优先级相同,自右向左,先指针加1再取内容
printf(" *cp is %c\n", *cp);
printf(" *cp++ is %c\n", *cp++);//*,++优先级相同,自右向左,先取内容再指针加1
//其实这里挺奇怪的 两者优先级相同,且是从右向左,那就应该是先++,但是我们这里可以这//样认为两者操作符没有挨着,所有遵循从左到右,先取值,再指针+1
printf(" *cp is %c\n", *cp);
printf(" ++*cp is %c\n", ++*cp);// *,++优先级相同,自右向左,先取内容再内容加1