c语言

C语言笔记

程序员除了素质一无所有

纯手工打造

独具匠心

Mr_zhang

  • 访问此博客
  • 好评
  • 非常好评
  • 极度好评

文章目录

  • linux目录结构:
    /:根目录
    /bin: /usr/bin:可执行二进制文件的目录
    /lib: /usr/local/lib:系统使用的函数库的目录
    /home:系统默认的用户家目录
    /usr/include:头文件所在的目录

  • 文件分类:普通文件,目录文件,设备文件,管道文件,链接文件
    文件权限:访问用户,访问权限
    访问用户:文件所有者,用户组,其他用户
    访问权限:读权限(r),写权限(w),可执行权限(x)

linux常用命令:
  1. 查看帮助文档:* --help , man * 手册
  2. 查看文件信息:ls -a所有 -l详细 -h人性化
  3. 输出重定向命令:>
  4. 分屏显示:more
  5. 管道:|
  6. 清屏:clear
  7. 切换工作目录:cd
  8. 显示当前路径:pwd
  9. 创建目录:mkdir
  10. 创建文件 :touch
  11. 删除文件 :rm
  12. 建立链接文件:ln -s软链接
  13. 查看文件内容:cat
  14. 文本搜索:grep
  15. 查找文件:find
  16. 拷贝文件:cp -r文件夹
  17. 移动文件:mv
  18. 获取文件类型:file
  19. 归档管理:tar
  20. 文件压缩解压:gzip -cvf 压缩 -xvf 解压
  21. 文件压缩解压:bzip2
  22. 查看命令位置:which

  • 软链接:
    1.类似于windows的快捷方式,软链接依赖于源文件存在
    2.如果源文件删除,软链接没有意义

  • tar只负责打包,解包,不进行压缩
    打包:tar -cvf xxx.tar 需要打包的文件
    解包:tar -xvf xxx.tar 需要解包的文件
    指定目录解包:tar -xvf xxx.tar -C 指定路径目录

  • 如何查看某个目录的大小:
    du ./目录 -h

  • gzip压缩tar包
    压缩:gzip [-r] xxx.tar 自动在当前目录生成:xxx.tar.gz
    解压:gzip [-d] xxx.tar
    压缩打包:
    tar -czvf xxx.tar.gz 所需文件
    bzip2压缩tar包
    压缩:bzip2 [-r] xxx.tar 自动在当前目录生成:xxx.tar.bz2
    解压:bzip2 [-d] xxx.tar
    bzip2压缩打包:
    tar -cjvf xxx.tar.dz2
    解压解包:tar -xjvf xxx.tar.dz2

  • 创建新用户:
    useradd -d /home/名称 用户名 -m
    更改密码:passwd 用户名

  • 曾登陆用户时间历史:last


创建一个用户(系统默认创建一个用户组,这个用户组的名字和用户的名字是一样的)
查看用户信息:cat /zhang/passwd
zhang:x:1000:1000:zhang,:home/zhang:/bin/bash
第一个1000代表是用户id
第二个1000代表是这个用户属于哪个组,组id


更改权限:chmod
chmod 777(4 2 1)文件名 可读可写可执行 r 4 w 2 x 1
u 所有者 g 用户 o 其他用户
chmod u+w 文件名

文件夹权限说明:

x:没有此权限,不允许用户进入
r:没有这个权限,无法查看文件夹的内容
w: 没有这个权限,无法新建和删除文件
查看当前日历:cal
显示或者设置时间:date
查看进程信息:ps -a显示终端的所有进程,包括其他用户的进程 -u显示进程的详细状态
-x 显示没有控制终端的进程
ps -aux | grep xxx 利用管道查询进程
杀死进程:kill -9强制杀死进程
动态进程:top
重启:reboot , init 6i
关机:shutdown now , init 0
查看或配置网卡信息:ifconfig


vi编辑器:

进入插入模式:i
退出插入模式:Esc
:wq 保存并退出
:q!强制退出
:x给文件加密码
:!命令 暂时离开vi,执行命令
i 光标位置当前处插入文字
o光标位置下方开启新行
O光标位置上方开启新行
I光标所在行首插入文字
A光标所在行尾插入文字
dd 从当前行剪切(删除)
yy 从当前行复制
p粘贴
u 撤销
gg去第一行句首
G去最后一行句首
xgg去指定一行句首
/xxx 在vi中查找xxx
n下一个
N上一个
:set nu 设定行号
:set nonu 取消行号

在linux下,指向用户编译的可执行程序,如果没有配置环境,在当前路径前面必须加 ./
system:
功能:在已经运行的程序中执行另外一个外部程序
参数:外部可执行程序的名字
返回值:不同系统返回值不一样
字符编码:
window默认支持的中文编码为gbk,gb2312,ANSI
linux默认支持的中文编码为 UTF-8

只在windows有效:我们使用的是windows命令
calc 计算器
mspaint 画图板
notepad 记事本
windows图形界面只有两个:Qt,MFC(微软vs)
vs中C语言可以嵌套汇编代码

C语言编译过程:预处理-----编译-----汇编-----链接
  1. 预处理:宏定义展开,头文件展开,条件编译等,同时将代码的注释删除,这里并不会检查语法
  2. 编译:检查语法,将预处理后文件生成汇编文件
  3. 汇编:将汇编文件生成目标文件(二进制文件)
  4. 链接:C语言写的程序是需要依赖各种库的,所以编译之后还需要把库链接到最终的可执行程序中去
    分布编译:
    预处理:gcc -E hello.c -o hello.i
    编 译 :gcc -S hello.i -o hello.s
    汇编 :gcc -c hello.s -o hello.o
    链接 :gcc hello.o -o hello_elf

交换文件说明:
1.vi写文件,没有保存就关闭,自动生成一个后缀为.swp交换文件,保存了前面写的内容
2.先恢复,再删除

寄存器是CPU内部最小的存储单位
  1. 如果需要运算,不能在内存中直接运算
  2. 如果需要运算,把内存中的数据加载到寄存器,在运算
  3. 把运算结果从寄存器移回内存中

数据类型的作用:告诉编译器定义这么一个类型的变量需要分配多大的空间
变量在使用前必须先定义,定义变量前必须有相应的数据类型
标识符命名规则:
标识符不能是关键字
标识符只能由字母,数字,下划线组成
第一个字符必须为字母或下划线
标识符中字母区分大小写
1Byte=8bit
%d : 以10进制的方式打印
%o :以8进制的方式打印
%x :以16进制的方式打印
int a=123 //以十进制方式赋值
int a=0123 //以八进制的方式赋值
int a=0xABC //以十六进制方式赋值


原码反码补码:

(1)存储1字节(8位)大小的数字(char)
原码(用户角度):原始的二进制
1.用户的数字分为正负数,符号位的存储
2.最高位为符号位:0代表为正数,1代表为负数
3.1000 0001 左边是最高位,右边是低位

+1 :0000 0001
-1 :1000 0001
+0 :0000 0000
-0 :1000 0000

原码存储导致2个问题:
1.0有两种存储方式
2.正数和负数相加,结果不正确(计算机只会加不会减)

以原码的方式来算:

反码(为了算补码)
0.正数的原码和反码是一样的
1.求原码
2.在原码的基础上,符号位不变,其他位取反(0为1,1为0)
+1 :0000 0001
-1 :1111 1110
+0 :0111 1111
-0 :1111 1111
反码存储导致1个问题:
1.0有两种存储方式(0不分正负,只有一种存储方式)

以反码的方式计算:
1 - 1 = 1 + (-1)
1 : 0000 0001
-1 : 1111 1110
1111 1111 = -0
计算机存储数字以补码方式存储(为了解决负数的存储)

补码:
1.正数的原码,反码,补码都一样
2.补码为其反码+1

+1 :0000 0001
-1 :1111 1111
+0 :0111 1111
-0 :1 0000 0000(因为8位,所以最高为丢弃) =0000 0000

以补码的方式来算:
1-1 = 1 + (-1)
1: 0000 0001
-1: 1111 1111
1 0000 0000 = 0
//0x81转化为二进制1000 0001,最高位为1,说明是负数

char a = 0x81;
printf("%d\n",a);

结果是 -127
补码:1000 0001
反码:1111 1110
原码:1111 1111 = -127

char类型为1字节(8位)范围:有符号: -128~127 无符号:0~255
-0系统当成 -128
%x,系统默认以4字节(32位)大小打印
%d,以有符号来打印
%u,以无符号来打印
赋值或运算,不能越界

sizeof的功能是计算一个数据类型的大小,单位为字节
int占4个字节 short占2个字节
字符型(char)
1.内存中没有字符,只有数字
2.一个数字,对应的一个字符,这种规则叫做ASCII
3.使用字符或数字给字符变量赋值是等价的

4.字符类型本质上就是1个字节大小的整形
逻辑运算符
!(逻辑非) :如果a为假,则!a为真,如果a为真,则!a为假
&&(逻辑与) :如果a和b都为真,则结果为真,否则为假
||(逻辑或) :如果a和b有一个为真,则结果为真,二者都为假时,结果为假

运算符和表达式
1.相除得到小数问题
a)两个整数相除,只是取整数,不会带小数

int a=1/2; // a=0

b)要想得到小数的结果,分子分母至少有一个是小数

 double a
 a=1.0/2;// a=0.5
 a=1/(double)2;

死循环
//{ }可以不写,不写只有第一个语句属于循环

while1{

}

//{ }必须写,语法

do
{

}while(1)

//{ }可以不写,不写只有第一个语句属于循环

for(;;)
{

}

循环的嵌套:任何一个循环的内部可以调用其他的循环

while()
{
  while()
  {
    do
     {
       for()
         {
          while()
            {
 
            }
          }    
      }while();
  }
}

int i=0;
int j=0;
int num=0;

/*
1.i=0;
2.判断i<10的条件,条件为真,执行for(j=0;j<10;j++){}内容,条件为假,跳出循环
3.执行{}内部

*/

for(i=0;i<10;i++)
{
for(j=0;j<10;j++)
{
num++;
}

}

break作用:

1.跳出switch语句
2.跳出循环
3.如果有多个循环,跳出最近的内循环
continue:属于循环,跳出本次循环,下次继续
goto:任意地方都可以使用,无条件跳转,勿滥用

数组

#include<stdio.h>
int main()
{
int a;//定义一个普通变量
//1.同一个{}内部,数组名不能和其他变量,其他数组名同名
//2.定义数组,[]最好是常量
int n=10;
int b[n];
//3.使用数组时,[]里 可以是变量,可以是常量,也可以是表达式
}


int main01()
{
//1.定义一个数组,数组内部有10个int类型的元素(变量)
//2.定义数组和定义变量的区别,名字后面加[],[]里面写元素个数
//3.数组在内存中是连续存储的
//4.通过下标访问数组的元素,下标从0开始,从0到9,没有a[10]这个元素
//5.有多少个[]就是多少维
int array[10];
array[0]=0;
array[1]=1;
array[2]=2;
}
  1. 编译器不是我们想象中那么智能,有些错误不能立马呈现
    错误:
  2. 编译错误(语法出错)
  3. 运行时错误(运行时异常),有些错误不能立马呈现

[ ]内部的变量或数组,不初始化,它的值为随机数

数组名

# include <stdio.h>
int main()
{
int a[10];
/*1.数组名是常量,不能修改*/
/*2.数组名的地址是数组首元素的地址*/
printf("a=%p,&a[0]=%p",a,&a[0]);
/*3.sizeof(数组名)测数组总大小 :10(元素)*4(一个元素4个字节)=40*/
printf("sizeof(a)=%lu\n",sizeof(a));
};

求三个数最大值

#include<stdio.h>
int main()
{
 int a=10;
 int b=20;
 int c=30;
 int max;
 max=a>b?a:b;
 printf("max=%d\n",max);
 max=(a>b?a:b)>c?(a>b?a:b):c;
printf("max=%d\n",max);
 return 0;
}

int main01()
{
 int a=10;
 int b=20;
 int c=30;
 int max;
 if(a>b)
{
 max=a;
}else if
{
 max=b;
}
if(max>c)
{
 printf("最大值为%d\n",max);
}else
{
 printf("最大值为%d\n",c); 
}
}

求数组最大值

#include<stdio.h>
int main()
{
 int a[]={10,-1,20,3,5,9,1,6,50,6};
 int n=sizeof(a)/sizeof(a[0]);
 int max=a[0];
 int i=0;
 for(i=1;i<n;i++)
 {
  if(a[i]>max)
{
 max=a[i];
}
}
printf("数组最大值为%d\n",max);
return 0;

}

数组翻转

Alt text

#include<stdio.h>
int main()
{
 int a[]={1,2,3,4,5,6,7,8,9};
 int n=sizeof(a)/sizeof(a[0]);  /*元素个数*/
 int i=0;     /*首元素下标*/
 int j=n-1;  /*尾元素下标*/
 int tmp;
 while(i<j)
 {
 /*交换a[i]和a[j]*/
  tmp=a[i];
  a[i]=a[j];
  a[j]=tmp;
  i++;   /*从左往右*/
  j--;   /*从右忘左*/
 
}
for(i=0;i<n;i++)
{
 printf("%d",a[i]);
}
printf("\n");
}

冒泡排序

![Alt text](./Image 1.png)

#include <stdio.h>
int main()
{
 int a[]={1,-1,2,-2,3,-3,4,-4,5,-5};
 int n=sizeof(a)/sizeof(a[0]);
 int i=0;
 int j=0;
 int tmp;
printf("排序前");
 for(i=0;i<n;i++)
{
 printf("%d",a[i]);
}
 printf("\n");

 for(i=0;i<n-1;i++)
 {
 for(j=0;j<n-1-i;j++)
 {
 if(a[j]>a[j+1])
 {
  tmp=a[j];
  a[j]=a[j+1];
  a[j+1]=tmp;
}
}
}
printf("排序后");
for(i=1;i<n;i++)
{
 printf("排序后:%d",a[i]);
}
printf("\n");

}

多维数组

#include<stdio.h>
int main()
{
 /*
 1、有多少[]就有多少维
 2、内存中没有多维,都只有一维,多维数组是特殊的一维数组
 3、定义了一个一维数组a[3],这个一位数组有3个元素,每个元素int[4]
 4、a[0],a[1],a[2]就是第0,1,2,元素的数组名
 5、二维数组用户可以理解为m行n列
*/
int a[3][4];
int i=0;
int j=0;
int num=0;
for(i=0;i<3;i++)
{
 for(j=0;j<4;j++)
{
 a[i][j]=num;
 num++;
}
}
 for(i=0;i<3;i++)
{
 for(j=0;j<4;j++)
 {
  printf("%d ,",a[i][j]);
 }
printf("\n");
}

}

二维数组数组名

#include<stdio.h>
int main()
{
 int a[5][10];
//1.数组名是常量,不能修改
//2.sizeof(数组名),测数组的总大小:5*int[10]=5*4*10=200
printf("sizeof(a)=%lu\n",sizeof(a));
//3.sizeof(a[0]),测的是第0个元素的大小:int [10]=4*10=40
printf("sizeof(a[0])=%lu\n",sizeof(a[0]));

//求行数(元素个数):总大小/每个元素的大小
int n=sizeof(a)/sizeof(a[0]);
printf("n1=%d\n");
//求列数
n=sizeof(a[0])/sizeof(int);
printf("n2=%d\n",n);
//行*列
n=sizeof(a)/sizeof(a[0])*sizeof(a[0])/sizeof(int)=sizeof(a)/sizeof(int);
 printf("n3=%d\n",n);

}

二维数组平均分

#include<stdio.h>
int main()
{
 int a[5][3]=
{
 {30,60,80,},
 {60,60,60},
  {77,88,99,},
 {88,66,77},
 {12,22,8}
};
 int i=0;
 int j=0;
 int sum =0;
 for(i=0;i<3;i++)
 {
  sum=0;
 for(j=0;j<5;j++)
 {
   sum+=a[j][i];
 }
}
printf("平均分:%lf\n",sum/5.0);
}

字符数组

#include <stdio.h>
int main ()
{
 /*
  1、C语言没有字符串类型,用字符数组模拟
  2、字符串一定是字符数组,字符数组不一定是字符串
  3、如果字符数组以字符'\0'('\0'等价于0)结尾,那么这个字符数组就是字符串
 */
  char a[10];
  char b[]={'a','b','c'};/*字符数组*/
  char c[10]={'a','b','c','\0'};/*字符串*/
return 0;

}

字符数组初始化

#include <stdio.h>
int main()
{
 char a1[]={'a','b','c'};/*字符数组*/
 printf("a1=%s\n",a1);/*乱码,因为没有结束符*/

char a2[]={'a','b','c',0};/*字符串*/
 printf("a2=%s\n",a2);

char a3[]={'a','b','c','\0'};
printf("a3=%s\n",a3);

char a4[]={'a','b','c','\0','h','e'};
printf("a4=%s\n",a4);/*abc*/

char a5[10]={'a','b','c'};/*前3个字符赋值为a,b,c,后面自动赋值为0*/
printf("a5=%s\n",a5);

/*常用初始化,使用字符串初始化,在字符串结尾自动加数字0*/
char a7[10]="abc";
printf("a7=%s\n",a7");

char a8[10]="abc";
printf("sizeof(a8)=%lu\n",sizeof(a8));/*字符串自动隐藏一个字符*/

char a9[10]="\0abc";
printf("a9=%s\n",a9);

/*\0后面最好别跟数字,有可能组成一个转义字符,'012'就是'\n'*/
char a10[10]="\012abc";
printf("a10=%s\n",a10);

}
sizeof()测数据类型大小,不会因为结束符提前结束

char a[100];
scanf("%s",a);
//a没有&,原因数组名是首元素地址,本来就是地址
printf("a=%s\n",a);

随机数产生

#include <stdio.h>
#include <stdlib.h>
#include <time.h>
int main ()
{
 /*
   先设置种子,种子设置一次即可
   如果srand()参数一样,随机数就一样
   srand(100);
   time(NULL)功能获取系统当前时间,由于时间会变,srand()也会改变
*/
 srand(        (unsigned int) time(NULL)     );
 int i=0;
 int num;
 for(i=0;i<100;i++)
 {
  num=rand();
  printf("num=%d\n",num);
 
 }
return 0;
}

scanf_字符串输入

![Alt text](./Image 2.png)


#include<stdio.h>
int main()
{
   char buf[100]={0};
   printf("请输入字符串buf:");
   scanf("%s", buf);/*不需要&,默认以空格分开*/
   printf("buf=%s\n",buf);

   char tmp[100]={0};
   printf("请输入字符串tmp:");
   scanf("%s", tmp);/*不需要&,默认以空格分开*/
   printf("tmp=%s\n",tmp);

   /*scanf()的缺陷,不做越界检查,此函数不安全*/
   char str[100]={0};
   printf("请输入字符串str:");
   scanf("%s", str);/*不需要&,默认以空格分开*/
   printf("str=%s\n",str);


}

gets

#include <stdio.h>
int main()
{
  char buf[100];
  /*
  gets()从键盘读取字符串,放在指定数组
  gets()允许有空格,不做越界检查,此函数不安全
 */
  gets(buf);
  printf("buf=%s\n",buf);
   return 0}

fgets

#include<stdio.h>
int main()
{
  char buf[100];
 /*
 从stdin(代表标准输入,键盘),读取内容
 如果输入内容如果大于sizeof(buf)-1,只取sizeof(buf)-1,放在buf所在的数组
 会把换行符也读进去

*/
 
  fgets(buf,sizeof(buf), stdin);
 printf("buf=%s\n",buf);

 return 0; 
}

puts

#include <stdio.h>
int main()
{
 char buf[]="hello";
/*把buf内容输出到屏幕,自动在屏幕加换行,是在屏幕加,字符串本身没有变量*/
 puts(buf);
 printf("%s",buf);
}

fputs

#include <stdio.h>
int main()
{
 char buf[]="hello";
/*往stdout(代表屏幕,标准输出)输出buf内容*/
 fputs(buf,stdout);


}

strlen

#include <stdio.h>
#include <string.h>
int main()
{
 char buf[]="hello";
 /*
  strlen需要使用返回值,返回值就是字符串长度
  从首元素开始,到结束符为止的长度,结束符不算(遇到'\0'结束)
*/
 int len=strlen(buf);
 printf("len=%d\n",len);
/*多了一个结束符\0*/
 printf("sizeof(buf)=%lu\n",sizeof(buf));

 char buf2="\0hello";
 len=strlen(buf2);
 printf("len2=%d\n",len);
 /*sizeof()测数据类型的长度,不会因为结束符提前结束*/
 printf("sizeof(buf2)=%lu\n",sizeof(buf2));

 char str[100]="mike";
 printf("strlen(str)=%lu\n",strlen(str));
 printf("sizeof(str)=%lu\n",sizeof(str));

 return 0;
}

字符串拷贝

#include<stdio.h>
#include<string.h>
int main()
{
 char src[100]="hello\0 mike";
 char dst[100]="aaaaaaaaaaa";
 /*功能:把src字符数组前12个字符的内容拷贝给dst所代表的数组*/
strncpy(dst,src,sizeof(src));
 printf("dst=%s\n",dst);
printf("dst=%s\n",dst+strlen("hello")+1);
 
return 0;
}
int main02()
{
  char src[100]="hello\0 mike";
  char dst[100];
 /*
 功能:把str字符数组前8个字符的内容拷贝给dst所代表的数组
*/
strncpy(dst,src,8);
 printf("dst=%s\n",dst);
printf("dst=%s\n",dst+7);
 return 0;
}
int main01()
{
 char src[100]="hello mike";
 char dst[100];
 /*功能:把src字符数组的内容拷贝给dst所代表的数组
  拷贝原理,从首元素开始,遇到结束符'\0'结束
*/
 strcpy(dst,src);
 printf("dst=%s\n",dst);
 
 char src2[100]="hello\0 mike";
 char dst2[100]strcpy(dst2,src2);
 printf("dst2=%s\n",dst);
 return 0;
}

字符串比较

#include<stdio.h>
int main()
{
 char s1[]="abc";
 char s2[]="abcd";
 int flag=strcmp(s1,s2);
/*
 指定比较前3个字符
 首先比较第一个字符,只要第一个字符大于对方,就已经比对方大
 int flag=strncmp(s1,s2,3);
*/
 if(flag>0)
{
 printf("[%s]>[%s]\n",s1,s2);
}
else if(flag<0)
{
 printf("[%s]<[%s]\n",s1,s2);
}
else
{
 printf("[%s]==[%s]\n",s1,s2);
}
}

sprintf

#include <stdio.h>
int main()
{
 int a=10;
 char ch='b';
 char buf[]="hello";
/*格式化一个字符串,把这个字符串输出到屏幕*/
 printf("a=%d,ch=%c,buf=%s\n",a,ch,buf);
 /*格式化一个字符串,把这个字符输出(保存)指定的数组*/
 char dst[100];
 sprintf(dst,"a=%d,ch=%c,buf=%s\n"a,ch,buf);
 printf(@@%s@@,dst);
}

ssanf

#include <stdio.h>
int main()
{
char buf[]="1 2 3";
 int a,b,c;
/*从buf中以指定的格式提取内容*/
 sscanf(buf,"%d %d %d",&a,&b,&c);
 printf("a=%d,b=%d,c=%d\n",a,b,c);

 /*提取整形最方便*/
 char str[]="a=1,b=2,c=3";
 a=0;b=0;c=0;
 sscanf(str,"a=%d,b=%d,c=%d",&a,&b,&c);
 printf("a1=%d,b1=%d,c1=%d\n",a,b,c);

char str2[]="1,2,3";
a=0;b=0;c=0;
sscanf(str2,"a=%d,b=%d,c=%d",&a,&b,&c);
printf("a2=%d,b2=%d,c3=%d\n",a,b,c);

/*提取字符串,默认以空格分隔,可以提取*/
char tmp[]="abc mike 250";
char m[10],n[10];k[10];
/*不用&,数组名就是首元素地址*/
 sscanf(tmp,"%s %s %s",m,n,k);
printf("m=%s, n=%s, k=%s\n",m,n,k);

char tmp1[]="abc,mike,250";
char m1[10],n1[10];k1[10];
/*不用&,数组名就是首元素地址*/
sscanf(tmp1,"%s,%s,%s",m1,n1,k1);
printf("m1=%s, n1=%s, k1=%s\n",m1,n1,k1);
 return 0;

}
int main01 ()
{
 int a ,b c ;
 printf("请输入三个数");
 scanf("%d %d %d",&a,&b,&c);
 printf("a=%d,b=$d,c=%d\n",a,b,c);
 return 0;
}

字符串查询

#include<stdio.h>
#include <string.h>
int main()
{
char buf[]="abcdefg"
/*
  在buf中查询字符d,如果找到,返回d所在位置的地址
  如果查询失败,返回NULL

*/
 char *p=strchr(buf,'b');
if(p==NULL)
{
 printf("查询失败\n");
}
else
{
printf("p=%s\n",p);
}

}

字符串切割

#include<stdio.h>
#include<string.h>
int main()
{
 char buf[]="abc,mike,250";
 char tmo[];
 strcpy(tmp,buf);
 char *p=strtok(tmp,",");
 while(p!=NULL)
{
  printf("p=%s\n",p);
 p=strtop(NULL,",");
}
}
int main01()
{
  char buf[]="abc,mike,250";
 char tmp[];
 strcpy(tmp,buf);
 /*
  第一次调用
 第一次参数写源字符串,第二次参数写切割字符
 返回值就是切割后的字符串
 在匹配切割字符的地方,换成结束符
 使用strtok()会破坏原来字符串的结构
 如果没有切割成功,返回NULL
*/
printf("before,buf[3]=%d\n",buf[3]);
char *p=strtok(tmp,",");
/*第二次调用,第一个参数需要写NULL*/
p=strtok(NULL,",");
printf("p2=%s\n",p);


printf("p=%s\n",p);
 printf("buf=%s\n",buf);
 printf("buf[3]=%d\n",buf[3]);
return 0;
}
  • 无参无返回值函数

#include <stdio.h>
void fun ()/*等价于void fun(void)*/
{
 /*
 无参无返回值函数的定义
 1、没有返回值,用void关键字修饰
 2、没有参数,也可以用void,或者为空
 3、第一个void代表返回值,fun代表函数名字,()内部代表参数,空就是无参,{}就是函数体
 4、同一个文件不能出现同名函数
 5、不同的函数,内部的变量是没有关系的
 函数内部,包括()内部的变量,只有在调用时分配空间,调用完毕自动释放
*/
printf("我是无参无返回值函数的定义\n");
 int a=10;
 int b=20;
 printf("a+b=%d\n",a+b);
}
int main()
{
 /*
  1、除了main函数外,其他函数只有调用了才起作用
  2、函数的调用不可能出现void关键字
  3、无参无返回值函数的调用格式:函数名字()
*/
   fun();
return 0}
  • 有参无返回值

#include <stdio.h>
/*
有参无返回值函数的定义
 1、定义函数的参数就做形参,形参的格式必须为:类型+变量,不能赋值
 2、函数没有调用,形参变量不会分配空间,函数调用完毕,形参变量自动释放
*/
 void fun1(int a)
{
 printf("a=%d\n",a);
}
/*如果形参有多个,用逗号","隔开*/
 void fun2(int a,char ch,char buf[100])
{
 printf("a=%d,ch=%c,buf=%s\n",a,ch,buf);
}

int main()
{
 int a =10;
/*
有参无返回值函数的调用
 1、如果函数定义室友参数,调用时必须传参
 2、有参函数调用形式:函数名(匹配类型参数)
 3、匹配类型参数可以是变量,常量,表达式
 4、函数调用时传的参数,叫做实参
 5、只能把实参的值传递给形参,不能反过来,单向传递
*/

 fun1(10);
 int i=11;
 fun1=(i);
 fun1(1>2);

 /*6、函数的形参有多个,传参也用逗号","隔开*/
 fun2(10,'a',"hello");

}
  • 无参有返回值

#include<stdio.h>
/*
无参有返回值函数的定义
 1、第一个int代表函数的返回类型,函数内部通过return返回
 2、return作用终止当前的函数
 3、返回值可以是常量,变量,表达式
*/
int fun()
{
 return 10;   /*返回常量*/
}

int fun1()
{
   int a=11;
  return a;   /*返回变量*/
}

int fun2()
{
 int a=10int b=20;
 return a>b?a:b;   /*返回表达式*/
}
/*
4、函数返回值只能返回一个,不能返回多个
5、如果return的类型和函数返回类型不一致,以函数返回类型为准
*/
int fun3()
{
  return 9.5555;
}
int main()
{
/*
1、如果函数有返回值,这个返回值可以不用
 2、如果使用返回值,最好要定义匹配类型的变量来接收/
*/
 fun(); 

int a=fun();
printf("a=%d\n",a);

 int b;
 b=fun();
 printf("b=%d\n",b);

 printf("tmp=%d\n",fun());

 printf("fun1=%d\n",fun1());
 
 printf("fun2=%d\n",fun2());

 printf("fun3=%d\n",fun3);

 return 0;
}

return 和 exit

#include <stdio.h>
#include <stdlib.h>
int fun()
{
  printf("fun\n");
 exit(250;  /*结束整个程序,结束进程*/
}
/*
 return主函数(main),程序结束
 return其他函数,程序不结束
*/
int main()
{
 while(1)
 {

}
return 0;  /*return作用:中断函数,中断main,程序就结束*/
printf("main\n");
}

声明和定义的区别

#include <stdio.h>
/*
 1. 编译器很笨,在main函数调用其他函数,只会往前找函数的定义
 2. 如果找不到函数的定义,就找函数的声明,如果没有声明,c编译器会警告,c++会出错
 3. 如果没有定义,直接调用一个不存在的函数,会报错
 4. 如果定义没有放在main函数前面,那么在调用前需要声明,声明加不加extern是一样的
 5. 一个函数只能定义一次,可以声明多次
 6. 声明的形参变量名和定义的形参变量名可以不一样
 7. 可以只声明,不定义函数(无意义),此函数不能调用
*/
/*函数的声明,告诉编译器,这个函数是有意义的,只是放在别的地方*/
 int my_strlen(char buf[]);
/*8、声明时,形参变量名可以不写,可以只写类型,定义时,形参变量名一定要写*/
 void fun(int a,int, int);

 int main()
{
  printf("len=%d\n",my_strlen("0123456789"));
 return 0;
}
int my_strlen(char str[])
{
  int i=0;
 while(str[i]!='\0')
{
 i++;
}
return i;
}

指针

![Alt text](./Image 3.png)

![Alt text](./Image 4.png)

#include <stdio.h>
int main()
{
/*
1、指针也是一个变量
 2、p是一个变量,p的类型是int *
*/ 
 int *p;
p=123;
printf("%d\n",p);

/*2、指针指向谁,就把谁的地址赋值给指针*/
int a=10;
p=&a;  /*p保存了a的地址*/
/*%p打印地址,是以16进制方式来打印*/
printf("%p, %p\n",p,&a);

/*
3、直接操作指针变量本身没有意义
4、需要操作*p,操作指针所指向的内存
*/
*p=100;   /**p相当于a*/
printf("%d, %d\n",*p,a);
 return 0;

}

![Alt text](./Image [5].png)

#include<stdio.h>
int main()
{
 int a=10;
 int *p=&a;
 *p=111;
 
 int *q;
 q=p;
 *q=222;
 printf("&a=%p,p=%p,q=%p\n",&a,p,q);
 printf("a=%d,*p=%d,*q=%d\n",a,*p,*q);
 return 0;
}

野指针:这个指针变量保存了一个没有意义(非法)的地址

![Alt text](./Image [6].png)

#include <stdio.h>
int main()
{
 int *p;
 p=0x1234;
 printf("p=%d\n",p);
/*
  *p=100;错误
 1、只有定义后的变量,此变量的地址才是合法地址
 2、野指针就是保存没有意义地址的指针变量
 3、操作野指针变量本身不会有任何问题
 4、操作野指针所指向的内存才导致段错误

 p=0x1234;没有定义0x1234;
 可能有一个叫0x1234的变量,*p=100就是操作这个0x1234变量,无法操作,所以出现段错误

*/
 *p=100;
 
 return 0;
 
}

空指

#include <stdio.h>
int main()
{
  /*
  1、空指针,就给指针变量赋值为NULL
  2、NULL就是数字0
*/
 
 int *p=NULL;
 
 int a=11;
 p=&a;
 if(p!=NULL)
{
 *p=100;
}
 return 0;
}

指针大小

#include <stdio.h>
int main()
{
 /*
 1、32位编译器用32位大小(4字节)保存地址
 2、64位编译器用64位大小(8字节)保存地址
*/
 int a=sizeof(int *);
 int b=sizeof(int *);

 double *p;
 int c=sizeof(p);
 printf("a=%d,b=%d,c=%d\n",a,b,c);
 return 0;
}

多级指针

![Alt text](./Image [7].png)

![Alt text](./Image [8].png)

[ ]不是数组专属
#include <stdio.h>
int main()
{
 int a=10;
 int *p=&a;
 /*通过指针简洁操作a的内存*/
 *p=111;
 
 printf("a=%d\n",a);
 printf("*p=%d\n",*p);
/* *p等价于*(p+0),同时等价于p[0]*/
 printf("p[0]=%d\n",p[0]);
/* *p等价于p[0],操作的是指针所指向的内存*/
 p[0]=250;
 printf("a2=%d\n",a);
 return 0;
}

万能指针

![Alt text](./Image [9].png)

#include <stdio.h>
int main()
{
  /*
  1、不可以定义void类型的普通变量,不能确定类型
  2、可以定义void *变量,void *指针也叫万能指针
  3、void *可以指向任何类型的变量
*/
 void *p=NULL;
 
 int a=10;
 p=&a;
 *((int *)p)=222;  /*只保存了a的首地址*/
 printf("*p=%d\n",*((int *)p));

}

指针步长

#include <stdio.h>
int main()
{
 /*
 1、指针的加法,不是传统的加法
 2、步长由指针指向的数据类型决定
*/
 int a;
 int *p=&a;
 printf("p=%d,p+1=%d\n",p,p+1);

 char b;
 char *q=&b;
 printf("q=%d,q+1=%a\n",q,q+1);
}

const修饰的指针

#include <stdio.h>
int main()
{
 /*
 1、指针变量
 2、指针所指向的空间
*/
 int a =10;
 int *p1=&a;
 *p1=100;
  p1=NULL;

 /*const 修饰* ,代表指针所指向的内存是只读的*/
 const int *p2=&a;
 /* *p2=100;不能操作*/
 p2=NULL;
 
/*const 修饰* ,代表指针所指向的内存是只读的*/
 int const *p3=&a;
 p3=NULL;

 /*const 修饰指针变量,代表指针变量的值为只读*/
 int * const p4=&a;
 *p4=100;
  /* p4=NULL; 不能操作*/ 

 const int * const p4=&a;

 return 0;

}

指向数组首元素的指针

![Alt text](./Image [10].png)

#include <stdio.h>
int main()
{
 int a[10]={1,2,3,4,5,6,7,8,9,10};
 
 int *p=NULL;
/*p指针变量指向首元素*/
 p=&a;
 p=a;
  
 int i=0;
 for (i=0;i<10;i++)
 {
 printf("%d,",*(p+i));
 /*
 等价于
  printf("%d",a[i]);
  printf("%d",p[i]);
  p[i]等价于 *(p+i),操作都是指针所指向的内存
*/

} 
 printf("\n");
}

通过指针加法访问指针元素

#include <stdio.h>
int main()
{
 int a[10]={1,2,3,4,5,6,7,8,9,10};
/*定义一个指针,指向首元素*/
 int *p=&a[0];

 int n=sizeof(a)/sizeof(*a);
 int i=0;
 for(i=0;i<n;i++)
 {
 printf("%d, ",*p);
 p++;
}
 printf("\n")/*定义一个指针,指向尾元素*/
 int *q=&a[n-1];
 q=a+n-1;
 for(i=0;i<n;i++)
{
 printf("%d, "*q);
 q--;
}
printf("\n");
}
  • ####指针数组,它是数组,每个元素都是指针
  • ####数组指针,它是指针,指向数组的指针
指针数组

![Alt text](./Image [11].png)

#include <stdio.h>
int main()
{
 int a[3]={0,1,2};
 /*指针数组,它是数组,每个元素都是指针*/
  int *p[3];
 p[0]=&a[0];
 p[0]=a;
 
 p[1]=&a[1];
 p[2]=&a[2];

 int n=sizeof(p)/sizeof(p[0]);
 int i=0;
 for(i=0;i<n;i++)
 {
   p[i]=&a[i];
}
 for(i=0;i<n;i++)
 {
 printf("%d\n",*p[i]);
}
 return 0;
}


int main01()
{
 int a =10;
 int b =20;
 int c =30;
 
 int *p1=&a;
 int *p2=&b;
 int *p3=&c;
 
 return 0;
}

值传递

![Alt text](./Image [14].png)

#include <stdio.h>
void swap(int m,int n)
{
 int tmp;
 tmp=m;
 m=n;
 n=tmp;
 printf("m=%d,n=%d\n",m,n);
}
int main()
{
  int a=11;
 int b=22;
 swap(a,b);  /*值传递,形参的修改不会影响到实参*/
 printf("a=%d,b=%d\n",a,b);
 return 0;
}

地址传递

![Alt text](./Image [13].png)

#include <stdio.h>
void swap(int *m,int *n)
{
int tmp;
tmp=*m;
*m=*n;
*n=tmp;
printf("m=%d,n=%d\n",m,n);
}
int main()
{
  int a=11;
int b=22;
swap(&a,&b);  /*地址传递,变量的地址*/
printf("a=%d,b=%d\n",a,b);
return 0;
}

形参中的数组

#include <stdio.h>

/*
 1、形参中的数组,不是数组,他是普通指针变量
 2、形参数组:int a[100000],int a[],int *a对编译器而已,没有任何区别
 3、编译器都是当作int *处理
  4、形参中的数组和非形参数组的区别:形参的数组是指针变量,非形参就是数组
 void printf_array(int a[100000])
 void printf_array(int *a)
*/

void printf_array(int a[])
{
 int i=0;
/*
 64位系统,sizeof(a),a是指针变量,结果为8
 sizeof(a[0])第0个元素,是int类型,结果为4
*/
 int n=sizeof(a)/sizeof(a[0]);
 printf("sizeof(a):%d\n",sizeof(a));
 printf("sizeof(a[0]):%d\n",sizeof(a[0]));
for(i=0;i<n;i++)
{
printf("%d, ",a[i]);
}
printf("\n");
 a=NULL; /*形参中的数组,不是数组,它是普通指针变量*/
}

int printf_array2(int a[0],int n)
 {
   int i=0;
   for(i=0;i<n;i++)
   {
   printf("%d, \n",a[i]);
}
 printf("\n");

}

int main()
{
 int a[]={1,-2,3,-4,5,-6,7,-8,9};
 int i=0;
 int n=sizeof(a)/sizeof(a[0]);
 printf_array2  (a,n);

 printf("排序前\n");
 
 /*冒泡排序*/
 int j=0;
 int tmp;
 for(i=0;i<n-1;i++)
 {
 for(j=0;j<n-1-i;j++)
  {
  if(a[j]>a[j+1]) 
  {
     tmp=a[j];
     a[j]=a[j+1];
     a[j+1]=tmp;
}
}
}
 printf("排序后\n");
  for(i=0;i<n;i++)
  {
 printf("%d, "a[i]); 
}
 printf("\n");
 return 0;
}

返回局部变量地址

![Alt text](./Image [14].png)

返回全局变量的地址

#include <stdio.h>
/*
 1、在{}外面定义的变量,就是全局变量,全局变量任何地方都能使用
 2、全局变量只有在整个程序结束后,才释放
*/
int a;
int *fun()
{
 return &a; /*fun()调用完毕,a不释放*/
}
int main()
{
 int *p=NULL;
 p=fun();
 
 *p=100;
 printf("*p=%d\n",*p);
 printf("a=%d\n",a);

 * ( fun() )=111;
 printf("a=%d\n",a);
}

字符串打印

#include <stdio.h>
int main()
{
 char str[]="hello mike";
/*
 1、%s,从首元素开始打印,直到结束符位置
 2、%s,操作的是指针所指向的内容
  printf("str=%s\n",str);
*/
 /*
  2、str是首元素地址,如果想打印str本身的值,%p,%x,%d,%o
*/
 printf("str=%p\n",str);
 /*
3、*str代表第0个元素,它是char
*/
 printf("str3=%c\n",*str);

 int i=0;
 while( str[i]!='\0')
 {
 printf("%c",str[i]);
 i++;
}
printf("\n");
}

字符指针

![Alt text](./Image [15].png)

#include <stdio.h>
int main()
{
  char str[]="hello";
 str[0]='1';
 *(str+1)='2';
 printf("str=%s\n",str);   /*  12llo;*/
 
 /*定义一个指针,指向首元素*/
 char *p=NULL;
 p=str[0];
 p=str;
 
 *p='a';
 p++;
 *p='b';
 printf("str=%s\n",str);  /* abllo*/
 printf("p=%s\n",p);   /*  bllo*/
 printf("p=%s\n",p-1);  /*abllo*/
 return 0;
}

字符串拷贝

#include <stdio.h>
int main()
{
 char buf[100];
 char *p=buf;
 /*
 p指向buf的首元素
 strcpy()是给p所指向的内存拷贝内容,字符串拷贝给了buf
*/ 
 strcpy(p,"hello mike abc");
 printf("p=%s\n,buf=%s\n",p,buf);
 return 0;
}

int main01()
{
 char p;
 /*
 错误
 1、不是给p变量拷贝内容
 2、给p所指向的内存拷贝内容
 3、p是野指针,给野指针所指向的内存拷贝内容,结果导致段错误
*/
 strcpy(p,"hello mike abc");
}

字符串拷贝函数

#include <stdio.h>
void my_strcpy(char *dst,char *src)
{
  int i=0;
  while(*(src+i)!='\0')
 {
  *(dst+i)=*(str+i);
   i++;
}
 /*结束符*/
 *(dst+i)=0;

}
int main()
{
  char src[]="hello mike";
  char dst[100];
  
   my_strcpy(dst,src);
   printf("dst=%s\n",dst);
}

文字常量去不能修改

![Alt text](./Image [16].png)

#include <stdio.h>
int main()
{
  /*1、字符串常量就是此字符串的首元素地址*/
  printf("s1=%p\n","hello mike");
 
  char *p1="hello mike";
  printf("p=%p\n",p1);
 
  char *p2="hello mike";
  printf("p2=%p\n",p2);
 /*2、字符串常量,文字常量区的字符串,只读,不能修改*/
  printf("*p1=%c\n",*p1);
   /*
 字符串常量,文字常量区的字符串,只读,不能修改
 *p1='a';  err
*/
/*
 char *p3="hello ";
 p3指文字常量区,不能修改
 strcpy(p3,"abc");
*/
 main01();

}
void fun()
{
 printf("fun s2=%p\n","hello mike");
}
int main01()
{
 /*
 1、每个字符串都是一个地址,这个地址是指字符串首元素地址
 2、字符串常量放在data区,文字常量区
*/
  printf("s1=%s\n","hello mike");
  printf("s1=%p\n","hello mike");
  printf("s3=%s\n","hello mike"+1);
  printf("s4=%c\n",*("hello mike"));
   fun();
}

字符串赋值和常量

![Alt text](./Image [17].png)

#include <stdio.h>
int main()
{
 /*
  1、p指针保存了"hello"的地址
  2、指针所指向的内存不能修改
*/
 char *p="hello";
 /*
  1、把"hello"一个一个字符放在buf数组中
  2、数组的元素可以修改
*/
 char buf[]="hello";
 return 0;
}

main函数形参使用说明

#include<stdio.h>
/*
 argc[]:它是数组,数组每个元素都是char*,每个元素都是字符地址
 argc:argc[]元素个数
 main()函数参数,需要用户传递
*/
int main(int argc,char *argc[])
{
 int i=0;
 for(i=0;i<argc;i++)
{
 printf("test=%s\n",argc[i])
}
  return 0;
}

查找匹配字符串出现次数

#include <stdio.h>
#include <string.h>
int main()
{
 char *p="11abcd11111122222abcd12666333abcd552652qqq";
 char i=0;
 char *tmp=NULL;
 
 while(1)
 {
  /*查找匹配字符串,如果找到,返回匹配字符串的地址,没有返回空*/
   tmp=strstr(p,"abcd");
   if(tmp==NULL)  /*没有找到*/
  {
   break;  /*跳出循环*/
}
 else   /*找到*/
  {
    i++;   /*累加*/
  /*重新设置寻找的起点*/
   p=tmp+strlen("abcd");
}
}
   printf("%s出现abcd的次数为:%d\n");
  return 0;
}

两头堵模型

#include <stdio.h>
#include <sr>
int main()
{
  char *p="      123456789     ";
  char *start=p;
  char *end=p+strlen(p)-1;

  while(*start==' '&&start!='\0')
  {
   start++;
}
  while(*end==' '&&end!=p)
 {
  end--;
}
  int n=end-start+1;
  printf("n=%d\n",n);
 int buf[100]="aaaaaaaaaaa";
 buf[n]=0;  /*结束符*/
 strncpy(buf,start,n);
 printf("buf=%s\n",buf);
}
  • 普通局部变量

  1. 在{}内部定义的变量就是局部变量
  2. 只有执行到定义变量的这个语句,系统才会给这个变量分配空间
  3. 当离开{},这个非static局部自动释放
  4. 局部变量的作用域在当前的{},离开{},无法使用此变量
  5. {}的普通局部变量。加不加auto关键字等价,普通局部变量也叫自动变量
  6. 不同的{}中,变量名字可以一样
  7. 如果static局部变量不初始化,它的值默认为随机数
  • static局部变量

  1. 在{}内部定义的变量就是局部变量
  2. static局部变量,是在编译阶段就已经分配空间,函数没有调用前,它就已经存在
  3. 当离开{},这个非static局部自动释放
  4. 局部变量的作用域在当前的{},离开{},无法使用此变量
  5. {}的普通局部变量。加不加auto关键字等价,普通局部变量也叫自动变量
  6. 如果static局部变量不初始化,它的值默认为0
  7. static局部变量初始化语句,只会执行一次,但是可以赋值多次
  8. static变量只能用常量初始化
  • 普通局部变量和static局部变量区别:

  1. 内存分配和释放
    a)普通局部变量只有执行到定义变量的语句才分配空间
    b)static局部变量在编译阶段(函数还没有执行),变量的空间已经分配
    c)普通局部变量离开作用域{},自动释放
    d)static局部变量只有在整个程序结束才自动释放
  2. 初始化
    a)普通局部变量不初始化,值为随机数
    b)static局部变量不初始化,值为0
    c)static局部变量初始化语句只有第一次执行时有效
    d)static局部变量只能用常量初始化
  • 普通全局变量(外部链接)

  1. 在{}外面(函数外面)定义的变量为全局变量
  2. 只有定义了全局变量,任何地方都能使用此变量
  3. 如果使用变量时,在前面找不到此全局变量的定义,需要声明才能使用
  4. 全局变量不初始化,默认赋值为0
  5. 声明只是针对全局变量,不是针对局部变量=
  6. 全局变量只能定义一次,可以声明多次
  7. 全局变量在编译阶段已经分配空间(函数没有执行前),只有在整个程序结束,
    才自动释放
  8. 分文件中不同文件,普通全局变量只能定义一次,可以声明多次
  9. 不同文件,普通全局变量只能定义一次,可以声明多次
  • 普通全局变量的定义和声明:
  1. 定义一个全局变量,建议初始化
    int a=10;
  2. -如果声明一个全局变量,建议加extern
    extern int a;
  • static全局变量(内部链接)

a)static全局变量和普通全局变量的区别就是作用域不一样(文件作用域)
b)extern关键字只适用于普通全局变量
c)普通全局变量,所有文件都能使用,前提需要声明
d)static全局变量只能本文件使用,别的文件不能使用
c)不同文件只能出现一个普通全局变量的定义
d)一个文件只能有一个static全局变量的定义,不同文件间的static全局变量,就算名字相同,也是没有关系的两个变量

  • 普通函数和static函数的区别

a)所有文件只能有一次普通函数的定义
b)一个文件可以有一个static函数的定义
c)普通函数所有文件都能调用,前提是使用前声明
d)static函数只能在定义所在的文件中使用

  • 普通局部变量

#include <stdio.h>
int main()
{
  int a=10;
 if(1)
 {
   int a=11;
   /*就近原则*/
   printf("a=%d\n",a);   /*a=11*/
 }
  /*
  1、if()的a只能在if{}中使用
  2、离开if{},if中的啊已经释放
*/
 printf("a=%d\n",a);
}
int main01()
{
 /*
  1、在{}内部定义的变量就是局部变量
  2、只有执行到定义变量的这个语句,系统才会给这个变量分配空间
  3、当离开{},这个非static局部自动释放
  4、局部变量的作用域在当前的{},离开{},无法使用此变量
  5、{}的普通局部变量。加不加auto关键字等价,普通局部变量也叫自动变量
  6、不同的{}中,变量名字可以一样
  7、如果static局部变量不初始化,它的值默认为随机数
*/
  int tmp=11;
  {
    int a=10;
    {
     a=11;  /*ok;还在作用域范围内*/
    }
  }
  /*a=11;  err; 离开作用域*/
   
  if(1)
   {
      int b=10;
   }

  for(int i=0;i<10;i++)
  {
   /*i只属于for语句,离开佛如就不能使用*/
   i=11;
   }
   /*
  printf("i=%d\n",i);
*/

}

static局部变量

#include <stdio.h>
void fun()
{
 int i=0;
 i++;
 printf("fun i=%d\n",i);
}
void static_fun()
{
/*
  1、在{}内部定义的变量就是局部变量
  2、static局部变量,是在编译阶段就已经分配空间,函数没有调用前,它就已经存在
  3、当离开{},这个非static局部自动释放
  4、局部变量的作用域在当前的{},离开{},无法使用此变量
  5、{}的普通局部变量。加不加auto关键字等价,普通局部变量也叫自动变量
  6、如果static局部变量不初始化,它的值默认为0
  7、static局部变量初始化语句,只会执行一次,但是可以赋值多次
  8、static变量只能用常量初始化
*/
  static int i=0;
 i++;
 printf("static_fun i=%d\n",i);
}
 int main()
 {
 fun();
 fun();
 fun();
  static_fun();
  static_fun();
  static_fun();
}

普通全局变量

#include<stdio.h>
/*
1、在{}外面(函数外面)定义的变量为全局变量
2、只有定义了全局变量,任何地方都能使用此变量
3、如果使用变量时,在前面找不到此全局变量的定义,需要声明才能使用
4、全局变量不初始化,默认赋值为0
5、声明只是针对全局变量,不是针对局部变量
6、全局变量只能定义一次,可以声明多次
7、全局变量在编译阶段已经分配空间(函数没有执行前),只有在整个程序结束,
     才自动释放
8、不同文件,普通全局变量只能定义一次,可以声明多次
*/
void fun2()
{
  extern int a;     /*声明时,不要赋值*/
  extern int a;
  extern int a;

 printf("fun2 a=%d\n",a);
}

int a=10;
void fun()
{
  a=11;
}
int main()
{
   fun();
  printf("a=%d\n",a);
  fun2();

}

c语言全局变量缺陷

#include<stdio.h>
int a;
int a;
int a=0;/*定义,其他是声明*/ 
int a;
int a;

/*有一次是定义,有三次是声明*/
int b;
int b;
int b;
int b;

/*
1、如果定义一个全局变量,没有赋值(初始化),无法确定是定义,还是声明
2、如果定义一个全局变量,同时初始化,这个肯定是定义
*/

int main()
{
  /*
 只有声明,没有定义,无法给变量赋值
 extern int b=10;
*/
 b=10;
 printf("b=%d\n",b);
return 0;
}

全局变量分文件

  • main.c
#include "test.h"
int main()
{
/*
使用函数前,声明函数
声明函数,extern可有可无
声明可以多次
*/
 test();
 a=111;
 b=222;
 return 0;
}
  • test.c
int a=0;
int b=0;
void test()
{
 a=10;
 b=20;
}
  • test.h
/*各种声明*/
extern int a;
extern int b;
void test();

![Alt text](./Image [18].png)

内存加载

在程序没有执行前,有几个内存分区已经确定,虽然分区确定,但是没有加载内存,程序只有运行时才加载内存:
text(代码区):只读,函数
data:初始化的数据,全局变量,static变量,文字常量区去(只读)
bss:没有初始化的数据,全局变量,static变量

当运行程序,加载内存,首先根据前面确定的内存分区(text,data,bss)先加载,然后额外加载两个区
text(代码区):只读,函数
data:初始化的数据,全局变量,static变量,文字常量区去(只读)
bss:没有初始化的数据,全局变量,static变量
stack:(栈区):普通局部变量,自动管理内存,先进后出的特点
heap: (堆区):手动申请空间,手动释放,整个程序结束,系统也会自动 回收,如果没有手动释放,程序也没有结束,这个堆区空 间不会自动释放

![Alt text](./Image [19].png)

  • ulimit -a : linux查看栈空间

栈越界

#include<stdio.h>
int main()
{
  /*语法上没有问题,栈区分配很大的内存,ulimit -a可以看到系统设置栈的上限是8M,越界了,导致段错误
    int a[1000000]={0};
*/
  int *p=(int *)malloc(10000000000000*sizeof(int));
   if(p==NULL)
{
printf("分配失败\n");
}
return 0;
}

内存操作函数 memset()使用

#include <stdio.h>
#include <string.h>
int main()
{
 int b[10]={0};
 /*处理一些代码,把b内部的元素改了*/
  /*b[10]={0};  err */
  int i=0;
  int n=sizeof(b)/sizeof(b[0]);
  for(i=0;i<n;i++)
   {
  b[i]=0;
}
  memset(b,0,sizeof(b));

 char str[10];
 memset(str,'a',sizeof(str));
 for(i=0;i<10;i++)
 {
 printf("%c, ",str[i]);
}
printf("\n");
return 0;

}
int main01()
{
  int a;
  memset(&a,0,sizeof(a));
  printf("a=%d\n",a);
 /*中间参数虽然是整形,但是以字符处理*/
  memset(&a,97,sizeof(a));
  printf("a1=%c\n",a);

 int b[10];
 memset(b,0,sizeof(b));
 memset(b,0,10 * sizeof(int));
 return 0;
}

memcpy

#include <stdio.h>
#include <string.h>
int main()
{
  char p[]="hello\0mike"; /*以字符串初始化,自动在默认隐藏一个结束符'\0';*/
  char buf[100]; 
  printf("sizeof(p)=%lu\n",sizeof(p));
  strncpy(buf,p,sizeof(p));
  printf("buf1=%s\n",buf);
  printf("buf2=%s\n",buf+strlen("hello")+1);

  memset(buf,0,sizeof(buf));
  memcpy(buf,p,sizeof(p));
  printf("buf3=%s\n",buf);
  printf("buf4=%s\n",buf+strlen("hello")+1);
  return 0;
}

内存重叠

![Alt text](./Image [20].png)

#include <stdio.h>
#include <string.h>
int main()
{
  int a[10]={1,2,3,4,5,6,7,8,9,10};
  int b[10];
  
  /*第三个参数是指拷贝内存的总大小*/
  memcpy(b,a,10 * sizeof(int));
 memcpy(b,a,sizeof(a));
 /*使用memcpy()最好别出现内存重叠*/
/*如果出现内存重叠,最好使用memmove*/
 /*memcpy(&a[2],a,sizeof(int));   err*/
  memmove(&a[2],a,5*sizeof(int));
}

比较memcmp

#include <stdio.h>
#include <string.h>
int main()
{
 int a[10]={1,2,3,4,5,6,7,8,9,10};
 int b[10]={10,2,3,4,5,6,7,8,9,0};
 if(flag < 0)
{
 printf("a<b\n");
}
 else if(flag>0)
 {
 printf("a>b\n");
}
else
{
 printf("a==b\n");
}
}
```## 标题

你好! 这是你第一次使用 **Markdown编辑器** 所展示的欢迎页。如果你想学习如何使用Markdown编辑器, 可以仔细阅读这篇文章,了解一下Markdown的基本语法知识。

## 新的改变

我们对Markdown编辑器进行了一些功能拓展与语法支持,除了标准的Markdown编辑器功能,我们增加了如下几点新功能,帮助你用它写博客:

 1. **全新的界面设计** ,将会带来全新的写作体验;
 2. 在创作中心设置你喜爱的代码高亮样式,Markdown **将代码片显示选择的高亮样式** 进行展示;
 3. 增加了 **图片拖拽** 功能,你可以将本地的图片直接拖拽到编辑区域直接展示;
 4. 全新的 **KaTeX数学公式** 语法;
 5. 增加了支持**甘特图的mermaid语法[^1]** 功能;
 6. 增加了 **多屏幕编辑** Markdown文章功能;
 7. 增加了 **焦点写作模式、预览模式、简洁写作模式、左右区域同步滚轮设置** 等功能,功能按钮位于编辑区域与预览区域中间;
 8. 增加了 **检查列表** 功能。
 [^1]: [mermaid语法说明](https://mermaidjs.github.io/)

## 功能快捷键

撤销:<kbd>Ctrl/Command</kbd> + <kbd>Z</kbd>
重做:<kbd>Ctrl/Command</kbd> + <kbd>Y</kbd>
加粗:<kbd>Ctrl/Command</kbd> + <kbd>B</kbd>
斜体:<kbd>Ctrl/Command</kbd> + <kbd>I</kbd>
标题:<kbd>Ctrl/Command</kbd> + <kbd>Shift</kbd> + <kbd>H</kbd>
无序列表:<kbd>Ctrl/Command</kbd> + <kbd>Shift</kbd> + <kbd>U</kbd>
有序列表:<kbd>Ctrl/Command</kbd> + <kbd>Shift</kbd> + <kbd>O</kbd>
检查列表:<kbd>Ctrl/Command</kbd> + <kbd>Shift</kbd> + <kbd>C</kbd>
插入代码:<kbd>Ctrl/Command</kbd> + <kbd>Shift</kbd> + <kbd>K</kbd>
插入链接:<kbd>Ctrl/Command</kbd> + <kbd>Shift</kbd> + <kbd>L</kbd>
插入图片:<kbd>Ctrl/Command</kbd> + <kbd>Shift</kbd> + <kbd>G</kbd>


## 合理的创建标题,有助于目录的生成

直接输入1<kbd>#</kbd>,并按下<kbd>space</kbd>后,将生成1级标题。
输入2<kbd>#</kbd>,并按下<kbd>space</kbd>后,将生成2级标题。
以此类推,我们支持6级标题。有助于使用`TOC`语法后生成一个完美的目录。



## 如何改变文本的样式

*强调文本* _强调文本_

**加粗文本** __加粗文本__

==标记文本==

~~删除文本~~

> 引用文本

H~2~O is是液体。

2^10^ 运算结果是 1024.



## 插入链接与图片

链接: [link](https://mp.csdn.net).

图片: ![Alt](https://avatar.csdn.net/7/7/B/1_ralf_hx163com.jpg)

带尺寸的图片: ![Alt](https://avatar.csdn.net/7/7/B/1_ralf_hx163com.jpg =30x30)

居中的图片: ![Alt](https://avatar.csdn.net/7/7/B/1_ralf_hx163com.jpg#pic_center)

居中并且带尺寸的图片: ![Alt](https://avatar.csdn.net/7/7/B/1_ralf_hx163com.jpg#pic_center =30x30)

当然,我们为了让用户更加便捷,我们增加了图片拖拽功能。


## 如何插入一段漂亮的代码片

去[博客设置](https://mp.csdn.net/configure)页面,选择一款你喜欢的代码片高亮样式,下面展示同样高亮的 `代码片`.
```javascript
// An highlighted block
var foo = 'bar';

生成一个适合你的列表

  • 项目
    • 项目
      • 项目
  1. 项目1
  2. 项目2
  3. 项目3
  • 计划任务
  • 完成任务

创建一个表格

一个简单的表格是这么创建的:

项目Value
电脑$1600
手机$12
导管$1

设定内容居中、居左、居右

使用:---------:居中
使用:----------居左
使用----------:居右

第一列第二列第三列
第一列文本居中第二列文本居右第三列文本居左

SmartyPants

SmartyPants将ASCII标点字符转换为“智能”印刷标点HTML实体。例如:

TYPEASCIIHTML
Single backticks'Isn't this fun?'‘Isn’t this fun?’
Quotes"Isn't this fun?"“Isn’t this fun?”
Dashes-- is en-dash, --- is em-dash– is en-dash, — is em-dash

创建一个自定义列表

Markdown
Text-to- HTML conversion tool
Authors
John
Luke

如何创建一个注脚

一个具有注脚的文本。1

注释也是必不可少的

Markdown将文本转换为 HTML

KaTeX数学公式

您可以使用渲染LaTeX数学表达式 KaTeX:

Gamma公式展示 Γ ( n ) = ( n − 1 ) ! ∀ n ∈ N \Gamma(n) = (n-1)!\quad\forall n\in\mathbb N Γ(n)=(n1)!nN 是通过欧拉积分

Γ ( z ) = ∫ 0 ∞ t z − 1 e − t d t &ThinSpace; . \Gamma(z) = \int_0^\infty t^{z-1}e^{-t}dt\,. Γ(z)=0tz1etdt.

你可以找到更多关于的信息 LaTeX 数学表达式here.

新的甘特图功能,丰富你的文章

Mon 06 Mon 13 Mon 20 已完成 进行中 计划一 计划二 现有任务 Adding GANTT diagram functionality to mermaid
  • 关于 甘特图 语法,参考 这儿,

UML 图表

可以使用UML图表进行渲染。 Mermaid. 例如下面产生的一个序列图::

张三 李四 王五 你好!李四, 最近怎么样? 你最近怎么样,王五? 我很好,谢谢! 我很好,谢谢! 李四想了很长时间, 文字太长了 不适合放在一行. 打量着王五... 很好... 王五, 你怎么样? 张三 李四 王五

这将产生一个流程图。:

链接
长方形
圆角长方形
菱形
  • 关于 Mermaid 语法,参考 这儿,

FLowchart流程图

我们依旧会支持flowchart的流程图:

Created with Raphaël 2.2.0 开始 我的操作 确认? 结束 yes no
  • 关于 Flowchart流程图 语法,参考 这儿.

导出与导入

导出

如果你想尝试使用此编辑器, 你可以在此篇文章任意编辑。当你完成了一篇文章的写作, 在上方工具栏找到 文章导出 ,生成一个.md文件或者.html文件进行本地保存。

导入

如果你想加载一篇你写过的.md文件或者.html文件,在上方工具栏可以选择导入功能进行对应扩展名的文件导入,
继续你的创作。


  1. 注脚的解释 ↩︎

  • 4
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值