C学习
程序:
高并发 低耦合 多线程
/**#include <stdio.h>
int main()
{
printf("hello world \n");
return 0;
}
#include <stdio.h>
int main()
{
printf("hello world!")
return 0;
}
printf("dadawdada5d\n\
dadawdadwadwad\n\ 第一个/配合N使用 第二个使程序认为上行是下行的延续
")
C语言的输出格式
%s字符串
%c字符
%d有符号的十进制数
%u无符号的十进制数
%p指针
%% %
%a 浮点数
_Bool 布尔类型
整数类型: short int / int / long int / long long int
浮点数类型: float / double / long double
字符类型: char
布尔类型: _Bool
枚举类型: enum
sizeof运算符
sizeof 运算符用于获取数据类型或表达式长度
-sizeof(object); //sizeof(对象)
-sizeof(type_name);//sizeof(类型)
-sizeof object; //sizeof 对象
signed 和 unsigned
存放负数 and 负数和0
signed short int
unsigned short int
signed int
unsigned int
signed long int
unsigned long int
signed long long int
unsigned long long int
bit b cpu能读懂的数据
内存机构中最小的单位
1byte = 8bit
1byte = 1111 1111 = 255 (FF)
pow算数函数
pow (2,32) 2的32次方
#include <stdio.h>
int main()
{
int result = pow(2,32) - 1 ;
printf("result = %d\n" , result);
return 0;;
}
char name[5] 声明字符串 且 字符串个数为5
name[0] = "D";
name[1] = "o";
name[2] = "n";
name[3] = "g";
name[4] = "Z";
e31 +3L
\l
char name[5] = {'D','o','n','g','Z'};
C语言的字符串 就是 字符数组
索引号从0开始
运算符
赋值运算符
方括号运算符
算数运算符 + - * /
1 + 2
操作数 + 运算符 + 操作数 双目运算符
+1 单目运算符
表达式 1 + 1 'a' + 'b' a + b
C语言的优先级和选择性
复合的赋值运算符
a = a + 1 -> a +=1
a = a - 1 -> a -=1
a = a * 1 -> a *=1
a = a / 1 -> a /=1
a = a % 1 -> a %=1
自增自减运算符
a++
++a
a--
--a
逗号运算符
a,b,c,d,e,f
强制转化类型
int a;
(double)a (flode)a (long)a
初始化数组
int a[10] = {0}
int a[10] = {1,2,3,4,5,6,7,8,9}
int a[10] = {1,2,3,4} 剩下的被系统初始化为0
指定初始化元素
int a[10] = {[3]=3 , [5]=5 ,[7]=7}
定义数组的结束符 '\0'
字符串处理函数
#include <string.h>
strlen 获取字符串的长度
strcpy strncpy拷贝字符串
strcpy(a,b) 把b的值copy给a
strcpy(c,"Copy Sucess!"); 把copy sucess 的值赋给数组c
strncpy限制copy字符串的长度
char a[] = "To Be Continues"
char b[40];
strncpy(a,b,5) 5=限定的字符长度为5
输出:To Be = 5byte
strcat strncat连接字符串
strucat限制连接字符串的字符长度
#include <stdio.h>
#include <string.h>
int main()
{
char str1[]="DongZai ";
char str2[]="Love's Han";
//strcat(str1,str2);
strncat(str1,str2,6);
printf("str1:%s\n",str1);
}
比较字符串
strcmp strncmp
int整形存储变量在内存中占用4个字节 char字节型存储变量在内存中占用4个字节
指针和指针变量
a -> 10001
b -> 10002
c -> 10003
d -> 10004
pa -> 11000
pf -> 11004
定义指针变量
类型名 *指针类型变量名
char *pa; //定义一个指向字符型变量的指针变量
int *pb; //定义一个指向整型的指针变量
取地址运算符和取值运算符
如果需要获取某个变量的地址,可以用取地址运算符(&)
char *pa = &a;
int *pb = &f;
如果需要访问指针变量指向的数据,可以使用取值运算符(*);
printf("%c,%d\n",*pa,*pb);
#include <stdio.h>
int main()
{
char a = 'C';
int b = 123;
char *pa = &a;
char *pb = &b;
printf("a = %c\n" , *pa);
printf("b = %d\n" , *pb);
*pa = 'D';
*pb =124;
printf("now a = %c\n" , *pa);
printf("now b = %d\n" , *pb);
printf("sizeof pa = %c\n" , sizeof(pa));
printf("sizeof pb = %d\n" , sizeof(pb));
return 0;
}
输出
a = C
b = 123
now a = D
now b = 124
sizeof pa = 8; //32架构的系统 输出为4 因为32位编译器int类型的4个字节 64位编译器8字节
sizeof pb = 8;
#include <stdio.h>
int main()
{
int a;
int *p = &a;
printf("请输入一个数:");
scanf("%d",&a);
printf("a = %d\n",a);
printf("请输入一个数:");
scanf("%d",p);w
printf("p = %d\n" ,*p);
return 0;
}
对比标准的下标法访问数组元素,这种使用指针进行间访问的方法叫指针法.
char a[] = {1,2,3,4,5,6,7,8,9};
char *p = a;
*(p+1) 这种意义相当于指向距离指针所在位置向前或向后的第n个元素
*p的位置与a[0]的位置相等,在p的基础上进行加减相当于去调用a[]数组中的其他元素
直接用指针定义字符串
指针和数组的区别
#include <stdio.h>
int main()
{
char str[]= "I love Han Han";
char *target = str;
int count = 0;
while(*target++ != '\0') *target++ 优先级 先+++ 后* !=比较 target++指定
下一个元素得位置 *target调用指针函数 相当于a[target]
来指向下一个字符中的内容 接着与'\0'数组结束符号进行判断,如果不是结束符号,那么
count+1 继续运行
{
countr ++
count++;
}
printf("togeter have string : %d",count);
return 0;
}
lvalue rvalue 左值和右值区别
数组名只是一个地址,而指针是一个左值.
指针数组和数组指针
int *p1[5]; ---------> 指针数组
int(*p2)[5]; ---------> 数组指针
指针数组
int *p1[]
int * int * int * int * int *
指针数组是一个数组 每个数组元素存放一个指针变量
int a = 1
int b = 2
int b = 3
int d = 4
int e = 5
int f = 6
int *p1[$] = {&a,&b,&c,&d,&e.&f}
int i;
指针数组;
指向数组一个特定的数组
相当于一个指针类型的数组
int *p[4]=
{
"",
"",
"",
"",
""
};
数组指针:
相当于用指针来表示数组中的第一个元素的位置
for (i=0;i<5;i++)
{
printf("%d\n",*(p+1));
}
指向数组的第一个元素 不是指向数组
int temp[] = {1,2,3,4,5,6,7,8,9};
int *p2[5]=&temp;
函数:
void func()
{
.....
}
void func()
{
count++;
}
int count;
int main()
{
func();
printf("%d",count)
}
按照程序执行从上往下的顺序 在函数后面定义变量 一定会报错的
这时可以用到一个函数
extern count
告诉程序这个变量在后面 等等在调用.
void func()
{
extern count;
count++;
}
int count;
int main()
{
func();
printf("%d",count)
}
这样就不会报错了
C语言的作用域
- 代码块作用域
- 文件作用域
- 原型作用域
- 函数作用域
代码块作用域:
从标志该符号块结束的??大括号处结束
定义: 当一个变量被定义的时候,编译器为变量中申请内存空间填充一定的值
声明: 当一个变量被声明的时候,编译器就知道该变量被定义在其他地方
局部变量即使定义又是声明
链接属性:
external 多个文件中声明的同名标识符表示同一个实体
internal 单个文件中声明的同名标识符表示同一个实体
none 声明的同名标识符被当成独立不同的实体
只有具备文件作用域的标识符才能拥有external或internal的链接属性,其他作用标识符都是none属性
test.c
#include <stdio.h>
char *c = "";
void a(void);
void b(void)
int main()
{
a();
b();
printf("%s\n",c)
return 0;
}
a.c
extern char *c;
void a(void)
{
c = "Niubi";
}
b.c
extern char *c;
void b(void)
{
c = "DongZai";
}
生存期:
-静态存储期 只有程序关闭 才会释放
-自动存储期 代码块作用域 在代码快结束的时候 就会被释放
具有文件作用域的变量属性静态存储期,函数也属
于静态存储期.属于静态存储期的变量在程序执行
期间将一直占据存储空间,直到程序关闭才释放
存储类型其实是指变量值的内存类型,C语言提供了5种不同的存储类型
-auto
-register
-static
-extern
-typedef
auto自动变量
在代码块中声明的变量默认的存储类型就是自动变量,使用关键字auto来描述
#include <stdio.h>
int main()
{
auto int i,j,k;
return 0;
}
#include <stdio.h>
int i;
int main()
{
auto int i;
return 0;
}
寄存器变量(register)
如果将变量声明为寄存器变量,那么你就不能通过取值运算符获取该变量的地址
startic 静态局部变量
在默认情况下所有的 所有声明的变量为auto类型
如果用static来声明变量, 是具有静态存储器,与全局变量一致,需要等待程序结束,才会释放
#include <stdio.h>
void func(void);
void func(void)
{
static int count = 0;
printf("count : %d\n",count);
count++;
}
int main()
{
int i;
for(i=0;i<10;i++)
{
func();
}
return 0;
}
count : 0
count : 1
count : 2
count : 3
count : 4
count : 5
count : 6
count : 7
count : 8
count : 9
可以看到结果,说明每一次调用func count是不变的,所以以此+1 一直到9
要是把static删除,则一直输出0
count : 0
count : 0
count : 0
count : 0
count : 0
count : 0
count : 0
count : 0
count : 0
count : 0
可以证明:通过定义静态存储器,与全局变量一样是静态存储类型,需要程序结束才能释放
static 和 extern
static关键字可以将具有external链接属性的标识符转化为internal链接属性,而external关键字是用于告诉编辑器这个变量
或者函数在别的地方以及定义过了,先去别的地方找找,不急着报错
-------------------------------------------------
#include <stdio.h>
int count; 可以看到count在这里只是声明没有定义 相当于去external
void func(void);
void func()
{
printf("count = %d\n",count);
}
------------------------------------------------
void func();
int count = 521; 这里的count定义了
int main()
{
func();
return 0;
}
-------------------------------------------------
auto 和 register 定义的变量具有自动存储期
startic 和 external 定义的变量具有静态存储期
递归编程思路
#include <stdio.h>
void recursion(void);
void recursion(void)
{
static int count = 10;
printf("Hello!\n");
if(count-- != 0){
recursion(); 函数中自己调用自己 加个判断
}
}
int main()
{
recursion();
return 0;
}
更灵活的内存管理放式
#include <stdlib.h>
malloc 申请动态内存空间
free 释放动态内存空间
calloc 申请并初始化一些列内存空间
realloc 重新分配内存空间
malloc
-void *malloc(size_t size);
malloc 函数向系统申请分配size个字节得内存空间,并返回一个指向这块空间的指针.
如果函数调用成功,返回一个指向申请得内存空间得指针,由于返回类型是void指针(*void),所以它可以被
转化成任意类型的数据,如果函数调用失败,返回值是Null.另外,如果size参数设计为0,返回值可能是null
但这并不意味着函数调用失败.
#include <stdio.h>
#include <stdlib.h>
int main()
{
int *p;
p =(int *) malloc(sizeof(int));
if(p == NULL)
{
printf("Error You Don't have many money!!");
exit(1);
}
printf("Please You Enter a number : ")
scanf("%d",p);
printf("You Enter number is %d", *p);
return 0;
}
free
函数原型
void free(void *pty);
free函数释放ptr参数指向的内存空间.该内存空间必须是由malloc calloc realloc 函数申请的.
否则,该函数将导致未定义行为,如果pty参数是NULL,则不执行任何操作.注意:该函数并不会修改ptr参数的值,
所以调用后它仍然指向原来的地方(变为非法空间).
#include <stdio.h>
#include <stdlib.h>
int main()
{
int *p;
p = (int *) malloc(sizeof(int));
if(p == NULL)
{
printf("You Don't Have many money : ");
exit(1);
}
printf("Please You Enter A number : ");
scanf("%d",p);
printf("You Enter number is : %d", *p);
free(p);
printf("You Enter number is : %d", *p);
//可以看到free释放完malloc 定义的指针p, 最终的指针内容被释放变为0
回显
Please You Enter A number : 123
You Enter number is : 123
You Enter number is : 0
导致内存泄露!!!
1.隐式内存泄漏(即用完内存块没有即使使用free函数释放)
2.丢失内存块地址 p = (int *)malloc(sizeof(int)); ---> 指向内存地址 int num = 520
*p = &num 一开始调用内存指向的地址被重新赋值找不到之前的地址,就会
产生丢失内存块地址
初始化内存空间
memset 使用一个常量字节填充内存空间
memcpy 拷贝内存空间
memmove 拷贝内存空间
memcmp 比较内存空间
memchr 在内存空间中搜索一个字符
memset(初始化的内存块,初始化的常量,内存块的尺寸)
calloc
void*calloc(size_t nmemb , size_t size)
calloc函数在内存中动态地申请nmemb个长度为size的连续内存空间
即申请的总空间为nmemb * size,这些内存空间全部初始化为0
等价式子
calloc()
int *p = (int *)calloc(8,sizeof(int));
malloc
int *p = (int *)malloc(8 * sizeof(int));
memset(p,0,8*sizeof(int));
}
~
~
realloc 是 memcpy的简化题
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(void)
{
int i;
int *p1 = NULL;
int *p2 = NULL;
p1 = (int *)malloc(10 * sizeof(int));
for(i=0;i<10 ;i++)
{
p1[i] = i;
//printf("%d\n",p1[i]);
}
p2 = (int *)malloc(20 * sizeof(int));
//把P1中的数据拷贝到P2中
memcpy(p2 , p1 , 11);
for(i = 0; i < 20 ; i++)
{
printf("%d\n",p2[1]);
}
free(p1);
free(p2);
memcpy的原型
realloc
void * realloc(void *ptr , size_t size)
realloc函数修改ptr指向的内存空间大小为size字节
如果新分配的内存空间大小比原来的大,则就内存块的数据不会发生改变
如果新的内存空间大小小于旧的内存空间,则会造成数据丢失,慎用!
C语言得内存布局规律
内存地址越高 内存地址高
内存地址越低 内存地址低
局部变量
动态申请中的内存空间
全局变量 未初始化
静态变量 未初始化
静态变量 初始化
全局变量 初始化
字符串常量
函数
------> 高地址 命令行参数和环境变量
------>
------> 栈
------>
------>
------> 未使用的内存空间
------>
------>
------> 堆
------>
------> bss段
------>
------> 数据段
------>
------> 代码段
-----------------------------------------------------------------------------------
代码段 (Text segment)
存放程序执行代码的区域,大小在程序运行之前就已确定,在内存区域中属于只读,
也可能存储一些变量,字符串
-----------------------------------------------------------------------------------
数据段 (Initialized data segment)
存放初始化的全局变量和局部静态变量
-----------------------------------------------------------------------------------
BBS段(Bss segment / Uninitialized data segment)
存放程序中未初始化的全局变量区域,BBS Block started by system
区段数据在程序执行前被自动初始化为0
实验
#include <stdio.h>
int main()
{
return 0;
}
gcc test.c -o test
size test
text data bss dec hex filename
803 248 8 1059 423 test
#include <stdio.h>
int globale_uninit_var;
int main()
{
return 0;
}
gcc test.c -o test
size test
text data bss dec hex filename
803 248 12 1063 427 test
可以看到bss的数值增加
说明 全局变量没有进行初始化 将会存储在bss段中
设置静态局部变量 发现bss的数值增加
将全局变量 和 静态局部变量进行初始化
text data bss dec hex filename
803 256 8 1067 42b test
发现data的值增加
说明数据段存储初始化的全局变量和静态局部
设置变量发现text的值变化
说明代码段存储变量和字符串.
text data bss dec hex filename
835 256 8 1099 44b test
-----------------------------------------------------------------------------------
堆
用于存放程序运行中的动态分配的内存段
大小不固定,可动态扩展或缩小,当进行调用malloc,calloc,realloc分配内存时
新分配的内存就被动态的加到堆上,当利用free等函数释放内存时,被释放的内存
从堆上被剔除.
栈 ----------> 代码执行的内存区域 --------------------> 通常与堆共享一片区域
局部变量,函数的参数,函数的返回值 ----> 栈
栈 和 堆 区别
申请放式
- 堆 由 程序员手动申请 例: malloc calloc realloc 调用分配内存
- 栈 由 系统自动分配 代码执行的内存区域 需系统分配代码执行区域
释放方式
- 堆 由 程序员手动释放 例: 程序员用free函数释放调用的内存分配
- 栈 由 系统自动释放 当程序结束时,系统自动释放程序分配的空间
生存周期
- 堆 生存周期由动态申请到程序员主动释放位置,不同的函数之间均可自由访问
- 栈 生存周期由函数调用开始到函数返回时结束,函数之间的局部变量不能互相访问
实验:
#include <stdio.h>
#include <stdlib.h>
int *func(void)
{
int *p = NULL;
p = (int *)malloc(sizeof(int));
if(p == NULL)
{
printf("Error!");
}
*p = 520;
return p;
}
int main()
{
int *p = NULL;
p = func;
printf("%d\n",*p);
free(p);
return 0;
}
一般来说在func中调用的内存分配空间赋予的值,是不会被main函数接收到,
但是在func中 return p;将分配的指针地址返回,main函数接收到地址信息,
就可以找到对应的函数对应的值.
宏定义
defined
字符串替换
文件包含 宏定义 条件编译 ---> :编译
带参数 和 不带参数
#define PI 3.14
整个程序PI值 3.14
宏定义一般都用大写字母组成
宏定义只是进行简单的替换,编译器不会对宏定义进行语法检查
宏定义不是说明或语句 不需要加;
#include <stdio.h>
#define PI 3.14 define 的作用域 到 undef上面为止
int main(void)
{
int r;
float s;
printf("请输入圆的半径:");
scanf("%d",&r);
#undef PI 3.1415926
s = PI * r * r ;
printf("圆的面积是 : %.2f\n",s );
}
*/
q