linuxC

1.linux编程(编写,编译,运行)
编程语言: 高级语言 (c c++ ) 给人看
机器语言: 可执行程序 11110001010 给机器
1.linux编辑器使用 gedit和vim
1.1 gedit
gec@ubuntu:~$ gedit demo.c
注意:
1.gedit 不支持 windows上的快捷键
2.gedit 打开一个文件 默认生成一个备份 ~结尾
1.2 vim
vim 有三种工作模式
命令模式(操作文本),插入模式(修改文本),底行模式(搜索、退出)
 
进入vim 默认是命令模式
[N]x:删除从光标位置开始连续光标
[N]dd :删除光标所在行
u(nudo) :撤回操作
[N]yy:复制光标所在行
复制:
y0: 将光标至行首字符拷入剪切板
y$: 将光标至行尾字符拷入剪切板
剪切:
d0: 将光标至行首字符拷入剪切板
d$: 将光标至行尾字符拷入剪切板
块复制:
:118,125y
p或P:粘贴
命令行模式 进入 插入模式
i 光标原位置 I 行首插入
a 光标后 A 行尾插入
o 下一行 O 上一行
 
从插入模式 按 esc 键 进入命令模式
 
从插入模式 按 esc 键 进入命令模式 再 输入“:” 进入 底行模式(底行出现 :)
 
在底行模式下,就可以保存退出
:q 退出没有修改文件
:q! 强制退出
:w 保存文件但不退出
:x 保存并退出<=>:wq
:w File 另存为File给出的文件都不退出
:r File (Read)读入File指定文件内容插入到光标位置- 2 -
输入 wq!
w 保存 q 退出 ! 强制
vi 光标命令
H J K L
← ↑ → ↓
:N 移动到光标地N行
1G 移动光标到底1行
G 移动到光标到最后一行
:set un 显示行号
:set nonu 不显示行号
vi 查找命令
/string 查找字符串
n 继续查找
N 反向查找
支持正则表达式:eg:/^the(以the开头的行) /end$(以end结尾的行)
vi 替换命令
:范围 ,$s/str1/str2/g(g:全文替换)
eg=> 10,15s/str1/str2/g
2.gcc 编译器的使用
gcc编译器工作过程 : c 预处理 编译 汇编 链接 可执行程序
预处理 : 处理头文件 ,展开宏定义 ,注释屏蔽
gcc -E demo.c -o demo.i
编译 : 把C语言程序往二进制方向转换 ,查找程序中的语法错误
gcc -S demo.i -o demo.s
汇编 : 把程序的地址按照一定的规律排列
 
gcc -c demo.s -o demo.o
链接 :链接某些库文件 (c语言 )
gcc demo.o -o demo
一步到位:
gcc demo.c -o demo
更简洁的方法:
gcc demo.c
默认生成一个 a.out (程序)
调试
gcc -g
3.执行程序- 3 -
 
练习
分别使用 gedit vim 编写一个小程序 并且在linux上运行出来
 
========================共享目录 ==================================
共享目录设置
1.在windows上新建一个文件夹
2.点击 虚拟机 ---》 点 设置 ----》 点 选项 ----》点 共享文件夹
右侧 选择 总是启用 ---》添加刚才新建的 share文件夹
linux 进入共享目录
 
2.linux 标准 c
1.任务运行的结果
linux系统运行程序,是支持多任务的,我们要知道每个任务运行的结果
返回值去表示,0 证明程序是正常的,负数程序异常
2.主函数传参
linux 系统运行程序 ,是可以传参数的
./demo
cd /mnt/hgfs/share
#include<stdio.h>
int main() //int 函数的类型
{
printf("hello world\n");
return 0;//程序正常运行
}
#include<stdio.h>
int fun()
{
}
int main(int argc ,char *argv[])
//int 函数的类型 argc 参数的总数 argv[]传入的具体参数内容
{
printf("hello world\n");
printf("argc is %d\n",argc);
printf("argv[0] is %s \n",argv[0]);- 4 -
执行结果:./demo yueqian
hello world
argc = 2
/*argv[0] = ./demo */
argv[1] = yueqian
3.函数声明
3.程序调用函数的时候要声明, 头文件,自定义函数
#include<stdio.h>
//计算 两个整数的和
int fun(int a,int b)
{
 
return a+b;
}
//函数封装
int main(int argc ,char *argv[])
//int 函数的类型 argc 参数的总数 argv 传入的具体参数内容
{
printf("hello world\n");
printf("argc is %d\n",argc);
printf("argv[0] is %s \n",argv[0]);
printf("argv[1] is %s \n",argv[1]);
fun();
return 0;//程序正常运行
}
4.注释
写代码的时候要适当的添加注释
单行注释 //注释...........
多行注释 /* */
5.运行程序,出现错误
gec@ubuntu:/mnt/hgfs/share$ file demo//file 查看文件
printf("argv[1] is %s \n",argv[1]);
fun();
return 0;//程序正常运行
}- 5 -
demo: ELF 32-bit LSB executable, Intel 80386,
version 1 (SYSV), dynamically linked (uses shared libs),for GNU/Linux 2.6.24,
BuildID[sha1]=0x317529d84b6cc2c6dee58a22c01c05c441fbd651,
not stripped
ELF 文件类型 linux系统上的可执行程序
 
executable 属性 可执行
 
Intel 80386 文件架构 X86
3.变量 (数据)
程序 = 数据 + 算法
数据 :数据的封装 数据类型 空间
1.变量数据的存储空间
内存规则:
1.程序一定要在内存运行
2.程序分配内存时,连续分配
3.内存分配时,具有不确定性
2.变量的类型
int char float double
类型分类
1.基本变量数据类型 (int char float double。。。。)
2.构造类(数组 ,结构 ,联合体。。。。)
3.指针类
4.空类型 void
5.布尔类型 bool (逻辑真true 逻辑假 false)
6.可移植型数据 uint32_t int
a 16 2
b 32 int 4
3.变量的赋值
1. = 赋值 == 等于
2.右值赋给左值 int a=10;
3.赋值符号 左右两边 数据类型 要一致
4.变量的定义
原型 : 变量类型 变量名
int a = 10;
申请一块连续的4字节空间,命名为a ,把10 写入到这块空间
注意
1.同一个函数中,不能出现相同的变量名
2.命名规则
/* 1.由字母数字 下划线 组成- 6 -
2.开头不能是数字
3.不能跟关键字相同 */
原码 : 数据的二进制形式
 
5 0000 0101
-5 1000 0101
反码
5 0000 0101 正数 的反码是他本身
-5 1111 1010 负数 符号位不变 数据位取反
补码
5 0000 0101 正数的补码不变
-5 1111 1011 负数的补码 是他的反码+1
总结
1.正数的原码 反码 补码 不变的
2.负数在计算机的存储方式是以补码的形式存储的
3.char类型数据的取值范围
char a ; unsigned char b;
-128~ 127 0~255
可以表示的数据个数都是 256个
 
5.变量的作用域生命周期
变量定义的位置 决定生命周期
1.局部变量 (临时变量)-》函数体内
只有在程序运行时才会分配空间
2.全局变量 --》函数体外定义
gcc编译时就已经给他分配内存了
 
/* 总结 :
1.主函数跟其他的函数是平行
2.
被调用的函数的形参实际就是被调函数的局部变量
 
实参就是主调函数的局部变量
3.在不同函数里面可以定义相同的变量名 (不推荐这样操作)
4.全局变量 它作用域是从上往下 */
 
6.常用变量所占空间
/* Linux C常用的变量类型
类型/编译器 16位编译器 32位编译器 64位编译器
void 0 0 0
char 1 1 1
short int 2 2 2
int 2 4 4- 7 -
位 bit 只能表示0/1 1bit
字节 BYTE=8bit 8bit
字 WORD=2BYTE 16bit
双字 DWORD=2WORD 32bit
*/
4.变量的输入输出
1.输出
printf()--》输出到终端 屏幕
2.输入
scanf()---》键盘---》程序
格式控制符
%d 有符号10进制整形
%u 无符号10进制整形
%o 无符号8进制
%x 无符号16进制
%c 字符
%s 字符串
%f 浮点
%e 科学计数法
%p 指针指向的地址
3.数据显示的格式控制
%f
%.4f 显示小数点后4位数据
占位符
%5d 占5个字符的位置 ,右对齐
%-5d 占5个字符的位置 ,左对齐
float 4 4 4
double 8 8 8
long 4 4 8
long long 8 8 8
long double 8 12 16
char* 2 4 8
int a,b;
scanf(“%d%d”,&a,&b);- 8 -
4.转义字符
\n 换行
\t 制表符
\77
\xa
\转义字符后面只能跟8进制和16进制数
 
5.运算符
点 数据
线 运算符
面 逻辑结构 (选择 判断 循环)
体 多个函数(程序)
1.按目标操作数分类:
1.单目运算符
--a ,a++,~a,a<<2,&a,....
2.双目运算符
a+b,a-b,,,a|b,a&b。。。。。。。。
3.三目运算符 (条件运算符)
a? b:c
a是真的吗(a成立吗)
如果成立 取b的值 如果不成立 取c的值
2.按功能操作数分类:
1.算数运算符
/% ++ --
a++ ,a自身加一 ,先用后加
++a ,a自身加一 ,先加后用
2.关系运算符
= < <= == != 条件判断
== 才是等于的意思
= 是赋值
数学写法: a>b>c (C语言中是错误的)
c语言 : a>b&&b>c
3.逻辑运算符
|| && !
(表达式1 || 表达式2)
条件1||条件2 有真则真 ,全假才假- 9 -
(表达式1 && 表达式2)
条件1&&条件2 有假则假 ,全真才真
4.位运算符
(bit ,比特 直接操作的二进制数,在硬件控制,经常大量使用 )
~ & | ^ >> <<
=============================位运算=============================
1.为啥学位运算
嵌入式开发领域,会接触到大量的硬件,找硬件的地址,操作硬件
51单片机 寄存器 8
位序 7654 3210
P0 0000 0001
2.在linux中,一般也是需要直接操作内存 ,也会大量使用位运算
3.位运算符(代码中不能直接使用二进制 ,需要转成 16进制)
 
~ 按位取反 ~(1111 0101) 0000 1010
& 按位与 1111 0101 & 1000 1111 1000 0101
1 & 0 0
1 & 1 1
0 & 0 0
| 按位或 1111 0101 | 1000 1111 1111 1111
1 | 0 1
1 | 1 1
0 | 0 0
^ 按位异或 1111 0101 ^ 1000 1111 0111 1010
1 ^ 0 1
1 ^ 1 0
0 ^ 0 0
0 ^ 1 1
右移 0100 0000 >> 2 0001 0000
<< 左移 0000 0010 << 2 0000 1000
4.应用
 
1.对一个数的某一位置1
1010 0100 |
0001 0000 1 0000 0001
1010 0100 | 1<< 4
int a ; 把a的第6位置1 ,其他位不变
a = a|1<<6
把a的第n位置1 ,其他位不变
a = a|1<<n 某一位置1
2.对一个数的某一位置0
1010 0100 第5位置0 其他位不变
1010 0100 &- 10 -
1101 1111
~1101 1111 0010 0000 1<<5
1010 0100 & ~(1<<5)
int a ; 把a的第6位置0 ,其他位不变
a = a&~(1<<6)
把a的第n位置0 ,其他位不变
a = a&~(1<<n) 某一位置0
3.对二进制数进行分割
1001 1011 / 0001 0000 1001
1001 1011 % 0001 0000 1011
0xAF 1010 1111 1010 A
1111 F
练习
把0xabcd 分割成 ab cd
1010 1011 1100 1101 0000 0001 0000 0000
4.对二进制数据进行拼接
1000 位序 3210
1011 位序 3210
 
1000 1011 位序 7654 3210
1000 << 4 1000 0000
1000 0000|0000 1011 1000 1011
0xa 0xb ---> 0xab
练习
0xa 0xb 0xc 0xd ---》0xabcd
作业
1、编写一个程序,此程序要求输入一个整数,然后打印出从输入的值(含)到比输入的值大10(含)的所有
整数
值(比如输入 5,则输出 5 到15)。要求在各个输出值之间用空格、制表符或者换行符分开。
2 、写出下面表达式运算后 a的值,设原来a=12。设 a和 n 已定义为整型变量。
(1) a += a
(2) a -= 2
(3) a *= 2+3
(4) a /= a+a
(5) a %= (n%2),n 的值为 5
(6) a += a -= a *= a
3、编写一个程序,该程序要求输入一个 float型数并打印概述的立方值。使用你自己设计的函数来计算
该值
的立方并且将它的立方打印出来。main 函数负责把输入的值传递给该函数。
4、编写一个程序,此程序要求输入天数,然后将该值转换为星期数和天数。例如输入18,则要求输
出:
18 days are 2 weeks, 4days.- 11 -
5、分析并解释以下程序的执行结果。
#include <stdio.h>
int main(void)
{
int a,b,c,d;
a=10;
b=a++;
c=++a;
d=10*a++;
printf(“b,c,d:%d,%d,%d” ,b,c,d);
return 0;
}
做完作业 预习控制流 (判断 循环 选择 )
5.特殊运算符
1.赋值运算符 =
方向:从右到左
左边不能是常量
 
左右两边数据类型相同
2.
复合赋值运算
+= -= *= /= %=。。。
a+=3
,a=a+3 a自身增3
3.
条件运算符
a
?b:c
x=b>c? b:c
求b c 的最大值
4.sizeof
计算数据大小 sizeof(int)
 
5.逗号运算符 ,
int a
,b,c ,d;
int x=
(a=10,b=20 ,a+b); x为30
6.return
主函数里面 结束main
子函数里面 数值 函数的返回值
int fun(int a ,int b)//求和
{
r
eturn a+b;
}
7.
地址运算符
& *
&
取址符 scanf(“%d”,&a);
 
char * p 定义的变量是指针类型
 
 
如果 *出现在程序中 * 的作用就是解引用(把地址里的数据取出来)
int a=10;
int *p=&a
 
p=20;
printf("%d\n",p);
8.分量运算符- 12 -
. ->
主要用在结构体里
.引用结构体变量的成员
->引用结构体指针变量的成员
1.算数运算符 / % 对数据进行切割
 
int a=1234;
int a1,a2; //a1 12 a2 34
a1=a/100;// 1234/100 12
a2=a%100;// 1234%100 余数34
100 就是1234 分割成 12 34的临界值
2.
自增,自减 ++ --
不要产生歧义
a++ a--
a+++b ?
(a++)+b
3.==
和 = 区分开
比较两个数是否相等,要注意是 ==
4.!逻辑非
一般 != 不等于
!(表达式) 取反
int x= !(10>5) x 0
5.&& ||
 
 
(表达式1 || 表达式2)
 
条件1||条件2 有真则真 ,全假才假
(表达式1 && 表达式2)
 
条件1&&条件2 有假则假 ,全真才真
 
提示:
 
表达式的值 就是 bool 只有 0 非零 两种
0
1
100 真
-100
6.数据自增自减的方法
a++
自身加1
a+=1 自身加1
a+1 a
的值是不变的
*/- 13 -
6.代码的逻辑结构
点 数据
线 运算符
面 逻辑结构 (选择 判断 循环)
体 多个函数(程序)
1.顺序结构
程序默认的结构
从上到下,代码逐句执行
2.分支结构 (选择 判断)
判断
if
()
else
选择
switch(2)
case 1
case 2
==============================分支结构(判断)======================
1.if else
原型 :
if(判断条件)
{
如果判断条件成立 ,就执行if下面的语句
}
else
{
如果判断条件不成立 ,就执行else下面的语句
}
2.if else if else if 。。。 else
原型:
if(判断条件1)
{
1
2
3
。。。。。
}
else if(判断条件2)
{
}- 14 -
else if(判断条件3)
{
}
 
。。。
else(不能省)
{
}
注意 :
1.两路分支结构,else是可以省略的,通过if判断条件控制if下的代码是否执行
else if 结构中 else是不能省略
2.大括号{}里面代表的是一个代码块
3.代码块里面如果只有一个语句,大括号可以省略 (不推荐省略)
4.多路分支,如果一个条件成立后,默认的它会从上往下再去判断的
5.if结构是可以嵌套使用
 
if
()
{
if
()
{
 
}
}
练习
1.用if判断 求 a b c 中的最大值 ,a b c 手动输入
 
2.输入3个正整数 ,if判断能否组成三角形,能组成什么样的三角形(直角 等腰 等边)
 
=================================分支结构--选择=====================
原型:
switch(选择条件)变量(整形 字符 枚举)
{
case 数值1:
//变量等于数值1 ,程序就从case 数值1 开始执行
br
eak;//结束选择语句
 
case 数值2:
br
eak;
case
数值3:
br
eak;
 
。。。。
default://如果上述条件都不满足 就从default开始执行
break;//可以省略
}- 15 -
注意 :
1.switch 选择的程序的入口 ,从上往下去执行
 
2.switch 结构中 break是可以跳出 (终止)选择结构
3.执行函数中,判断数值
补充 : 牢记 ‘a’ 'A' '0' ascii 码值
97 65 48
 
case 不光可以是一个固定的值 ,也可以是一个区间 1 ... 6 10 ... 50 'A' ... 'Z'
... 左右两边要有一个空格,只能有一个
练习
输入一个字符判断他是 小写字母 大写字母 ,数字
ascii 码值
3.循环结构
while
do{
}while();
for(;;);
goto (跳转语句)
定义 变量的时候注意 不要跟上面的关键字重名
=============循环结构=================
1. while 循环
原型:
while(循环条件)
{
//循环条件为真,执行循环体
}
 
1.判断循环条件是否成立 ,如果成立 执行循环体
不成立直接跳出循环
2.break 是可以直接杀死循环的
特例 while(1) //死循环
{
break;//可以杀死
}- 16 -
2.do—while循环
原型
do{
循环体语句;
}while(循环条件);//注意 !!!!要加 ;
先斩后奏 不管循环条件是否满足 先执行一遍循环体再说
3.for循环
原型
for(表达式1;表达式2;表达式3)
{
循环体;
}
表达式1 循环条件的初值
 
表达式2 循环条件的判断
 
表达式3 循环条件的改变
口诀:1 2 体 3 2 体 3 2体 3 2
int a = 5;
for(a = 5;a>0;a--)
{
循环体 ;
}
for 死循环 for(;;)
补充 : 1.运行代码的时候 结束死循环 ctrl + c
2.for循环初值表达式可以没有,循环变量的值根据之前的值操作
int a = 5
for(;a>0;a--)
{
循环体 ;
}
3.初值表达式位置 可以直接定义变量
 
for(int a = 5;a>0;a--)
{
循环体 ;
}
编译时有问题- 17 -
gec@ubuntu:/mnt/hgfs/share$ gcc for.c -o for
for.c: In function ‘main’:
for.c:16:2: error: ‘for’ loop initial declarations are only allowed in C99 mode
for.c:16:2: note: use option -std=c99 or -std=gnu99 to compile your code
 
 
解决方法
gec@ubuntu:/mnt/hgfs/share$ gcc for.c -o for -std=gnu99
 
4.循环是可以嵌套使用的
while(1)
{
while(1)
{
if()
}
}
 
for
()
{
for()
{
if()
}
}
 
内层循环就是外层循环的循环体
练习
1 打印 1-100的数字
2.打印100以内的偶数
3.打印99乘法表
1 11=1
2 12=2 22=4
3 13=3 23=6 3*3=9
9 19 。。。 99
 
4.goto 跳转语句
原型
标签 :
 
goto 标签;
不建议使用
1.造成代码的可读性降低,影响代码调试
2.会破坏内存的栈逻辑,可能导致不可预知错误
不建议使用不代表不能用
在硬件驱动代码中,用的比较多- 18 -
7.函数
1.什么是函数?
函数就像一个黑匣子,只需要直到输入 输出,即可用他。
特点
1.通过特定的函数传参,去实现功能,可以有一个返回值
2.模块化编程。(函数的封装思想)
2.函数的分类:主函数 子函数
1.主函数 main
程序的入口, 从主函数的第一行开始运行
2.子函数
功能函数,有我们自己编写封装,决定它的输入输出
子函数编写封装
//计算两个整数的和
int sum(int a, int b)// 函数头(返回值类型 函数名 参数列表(形参))
{
return a+b; //函数体
}
注意:
1.封装函数之前,添加一定的注释去解释你的函数用法 ,别人方便使用
2.在函数头
int 返回值类型 (return )
1.得到函数执行的结果
2.决定函数的类型
 
 
sum 函数名
 
取名的时候要复合C语言的命名规则 (回顾变量的命名规则)
 
函数名也是函数的入口地址
 
 
(int a ,int b)参数
 
实参要和形参类型一致,数量一致
 
练习
封装函数,实现比较两个数的大小 把两个中较小的数打印出来
void 没有返回值 (return 可以不写 如果要写返回空数据 return ;)
void * 返回一个任意类型的指针 int * char *- 19 -
3.主函数传参
int main(void)
{
}
主函数参数如果为空,就不能传参
int main(int argc ,char *argv[]) //int 函数的类型 argc 参数的总数 argv 传入的具体参数内容
{
 
}
int main(int argc ,const char **argv) //int 函数的类型 argc 参数的总数 argv 传入的具体参数内
{
}
带参数的main函数才可以运行程序时传参数
./demo 12 ewe 45
argc 4
argv[0] ./demo
argv[1] 12
argv[2] ewe
argv[3] 45
作用
1.argc判断 是否符合运行程序的参数个数的要求
2.通过argv 指定运行时的数据
网络编程 ip地址
。。。。。
argv 存放的是 字符串类型的数据
 
如果程序需要传递整形数据
atoi
#include <stdlib.h>
int atoi(const char *nptr);
int num=atoi(argv[1])
作业
 
实验三 2 3 4 5 6- 20 -
1.编写函数,把字符'0'---'9',转换为整形的0-9
int fun(char a)
{
ascii码值
}
2.做一个简易计算器
1.基本功能 + - * /
*2.退出 q
1+1
2
6/3
2
8.字符串处理
1.字符串它不是变量 字符才是变量
一般表示字符串
1.const char *p
2.char buf[10];
printf("hello\n");
hello 如果我们没有用数组存储,默认的是存放在常量区(只读区),是不能修改的
char *str=“hello”;
printf(“%s\n”,str);
char *str="hello";//默认的是存放在常量区(只读区)
printf("1.%s\n",str);
*str='a';
//1.hello
//Segmentation fault (core dumped) 段错误
printf("2.%s\n",str);
解决方法:
字符串不妨到常量区,可以用数组
2.字符串数组
1.啥是数组
可以存储同一类型数据的集合(容器)
char buf[10]={'h','e','l','l','o'} ;
char buf[10]="hello";//buf来面的数据
buf[0] h
buf[1] e
buf[2] l
buf[3] l
buf[4] o
buf[5] \0
buf[0]='a';- 21 -
练习
这里有一个字符串“helloworld”
修改成 “hellowuhan”打印出来
3.字符 字符串的输入输出
字符
输入 : scanf () getchar()
char x;
scanf(“%c”,&x) x=getchar();//只能输入单个字符
字符串
char buf[10]="\0";
输入 scanf() scanf("%s",buf); //碰到空格默认结束输入
gets() gets(buf)
输出 printf() printf(“%s\n”,buf);
puts() puts(buf);
4.字符串函数
1.字符串拷贝函数
#include <string.h>
char *strcpy(char *dest, const char *src);
::将指针src指向的字符串拷贝到指针dest所指向的地址,直到src中的'\0'结束
char *strncpy(char *dest, const char *src, size_t n);
::将指针src指向的字符串拷贝到指针dest所指向的地址,拷贝n个字节以后结束
2.清空字符串
#include <strings.h>
void bzero(void *s, size_t n);
::将指针s指向的字符串,前n个字节全部写成'\0'
3.比较字符串
include <string.h>
int strcmp(const char *s1, const char *s2);
::比较s1和s2指针指向的字符串是否完全一样
int strncmp(const char *s1, const char *s2, size_t n);
::比较s1和s2指针指向的字符串的前n个字节是否完全一样
比较结果:完全一样,返回数值0
s1>s2,返回大于0
s1<s2,返回小于0
4.查找字符串
char *strstr(const char *haystack, const char *needle);
::在指针haystack指向的字符串中查找needle指向的字符串,并且返回找到时的首地址
 
5.拼接字符串
输出: printf(“%c”,x) putchar(x); //- 22 -
#include <string.h>
char *strcat(char *dest, const char *src);
::将指针src指向的字符串接在指针dest指向的字符串的末尾
char *strncat(char *dest, const char *src, size_t n);
::将指针src指向的字符串中的n个字节接在指针dest指向的字符串的末尾
 
6.分割字符串
#include <string.h>
char *strtok(char *str, const char delim);
::将指针str指向的字符串中,第一个与delim指针指向字符串相同的进行分割
::从第二次分割开始 char str要写成NULL
9.数组
1.什么是数组?
变量 , 可以储存一堆相同类型数据的一个集合
2.数组的定义
char buf1[20]="hello";
int buf2[5]={1,2,3,4,5};
char int 数组的类型 (数组里面存放的数据类型)
buf1 buf2 数组的名字
20 5 可以存放的数据的个数
3.数组的初始化
char buf[5]="hello";//正确
char buf[]="hello";//正确
char buf[];
buf="hello"; //错误
char buf[3]="hello";//错误 数组容量小了
char buf[6];
buf="hello";//错误 不能直接把字符数据给到数组
字符串 赋值 到数组 要用 strcpy()
char buf[]={'h','e','o'};//正确
char buf[6];
buf[0]='a';
buf[1]='b';
...
buf[5]='e';//正确
int buf[5]={1,2,3,4,5};
int buf[]={1,2,3,4,5} ;
int buf[5];
buf[0]=1;
buf[2]=2;
4.数组元素的访问- 23 -
方式:通过数组下标去访问
int buf[5]={1,2,3,4,5};
buf[0] 1
buf[1] 2 改变buf[1]的值 buf[1]=20;
buf[2] 3
buf[3] 4
buf[4] 5
!!!!!!!!数组 []里面里数字的作用
1.定义数组的时候,决定存放数据的数量(数组的大小)
2.使用过程中 ,访问数组元素时,数字代表数组的下标
(从0开始的),决定数据所在数组中的位置
练习
把1-10 放到数组里,并且打印出来 for
 
5.数组的简单应用
1.数组元素支持运算符操作
+ - * /
 
int buf[5]={1,2,3,4,5};
buf[0] 1
buf[1] 2 改变buf[1]的值 buf[1]=20;
buf[2] 3
buf[3] 4
buf[4] 5
int sum = buf[0]+buf[1] sum=1+2
int buf2[]={8,9,6,5};
sum=buf[0]+buf2[3] 1+5==6
2.两个数组进行数据交换
int buf1[5]={1,2,3,4,5};
int buf2[5]={8,9,6,5,23};
int temp;
buf1[0] 1 buf2[0] 8
temp=buf1[0]; //1
buf1[0]=buf2[0];// buf1[0] 8 buf2[0] 8
buf2[0]=temp;//8 8
3.数组也可以作为函数的参数 (数组名)
int fun(int buf1[5],int buf2[5])
int fun( int *buf1,int *buf2)
{
int sum;
for(i=0;i<5;i++)
{
 
把“hello”放到数组里,打印出来- 24 -
sum=buf1[i]+buf2[i];
printf("sum is %d\n",sum);
}
}
int main()
{
int buf1[5]={1,2,3,4,5};
int buf2[5]={8,9,6,5,23};
fun(buf1,buf2);
return 0;
}
6.二维数组
int a[N];
N个int类型的变量的集合
在实际运用过程中,下标的取值是0~N-1
int b[N][M];
N个一维数组的集合,int()[M]的一维数组
在实际运用过程中,下标的取值是0~N-1 0~M-1
{} : (1)表示函数的边界
(2)控制语句,分支语句的边界
(3)数组的边界
二维数组的加减运算,都是以所包含的一维数组所占字节大小来运算的
 
10.指针
1.啥是指针?
地址 定义变量,申请内存,内存就有对应的地址
指针就是一个特殊的变量,指向地址
int a;
int *p=&a;
p * &
2.指针的定义
 
int a=10;
int *p=&a;
int 指针的类型(决定这个指针可以指向什么类型的数据地址)
指针的标志
p 指针的名称
= 指向 (赋值 地址赋值)
变量的地址- 25 -
注意
1.指针的类型要跟我们需要操作的变量的类型一致
2.指针命名复合c语言命名规范
3.定义一个指针后要给他一个方向
1.指向一个具体的变量的地址
2.指向空
int *p=NULL;
4.void *p=NULL
不叫空指针 ,通用指针 ,既可以执行 int数据地址 又可以执行char 类型数据地址
 
3.指针的应用
1.指向变量的地址
2.通过指针修改变量的值(解引用 )
*作用 (出现的位置)
出现的变量定义的时候,指针的标志 表明当前定义的变量是一个指针
出现的程序中间,就是解引用 , 就是把他指向的那块内存里的数据取出来
 
3.通过指针直接访问变量数据
4.指针可以进行加减操作的
地址加1的结果取决于指针类型的
char类型 地址+1
int类型 地址+4
4.指针与数组的关系
int buf[5]={1,2,3,4,5};
buf[0] 1
int *p=buf;//数组名代表数组的首地址 ,也是首元素的地址,不代表整个数组的地址
printf("buf[0]is %d\n",buf[0]) ;
printf("buf[0]is %d\n",buf[0]) ;
printf("p %d\n",p);
printf("buf[1]is %d\n",buf[1]) ;
printf("p %d\n",(p+1));
除了sizeof的情况以外,数组名都表示的是数组的首地址
所有变量,由左向右读取,判断符号优先级高低。
对于多维数组而言,有多少个维度,就需要多少个下标,才能表示数值
否则就是该数组中的某一个地址
int a[5][6];
有一个5个成员的数组,每个成员是6个元素
a[2][3]----数值
a[3] ---地址:&a[3][0]- 26 -
int b[3][5][6];
b[1][2][3]----数值
b[1][2] ----地址: &b[1][2][0]
b[1] ----地址: b[1][0] , &b[1][0][0]
指针数组:是一个数组
数组指针:是一个指针
() > [] > *
练习:
11.内存数据分布
重点: 堆,栈的概念,关键字static,const
gcc 最终编译成的可执行文件是一种elf文件
可以使用命令readelf来读取该文件的分布
readelf -a 可执行文件
1.重点:
.stack:栈空间 2M
IO缓冲区:遇见'\n'从缓冲区拿出来
.heap:堆空间 malloc,calloc
.data:已经初始化的静态数据 static,全局变量
.bss:未初始化的静态数据 static,全局变量
.rodata:常量区 const
.txt:代码段
2.特点:
.stack: 包含在函数中,并且没有static,const修饰的,都是栈里面的
有上限,而且容易被释放,
不同标记的变量可以重名
栈空间被定义的变量,仅限当前函数使用- 27 -
.heap:理论上讲虚拟内存最大都可以是堆空间,实际也有上限
上限根据程序的运行情况来判断
需要手动释放(不会受到标记的影响)
malloc,calloc,relloc
#include <stdlib.h>
void *malloc(size_t size); //申请size字节大小的堆内存
void free(void *ptr); //释放ptr指针指向的堆内存
void calloc(size_t nmemb, size_t size);//申请nmembsize大小的堆内存,被申请出的内存会全部清零
void *realloc(void *ptr, size_t size);//向ptr指向的堆内存添加size大小的内存
.data: 只有在程序结束时才会被释放
.bss: 只有在程序结束时才会被释放
.rodata :常量区中的数据不可改变
错误表示:
Segmentation fault (core dumped) 内核转储错误
(1)栈溢出
(2)野指针被赋值
(3)堆溢出
(4)常量区不可被修改
3.关键字static const
static:
(1)修饰局部变量时,表示只会被初始化一次,
只能够在当前函数中运行,并且在程序结束时才会释放
(2)修饰全局变量时,表示只会被初始化一次,
能够在当前.c文件中被任意函数调用,并且在程序结束时才会释放
(3)修饰函数时,表示函数
能够在当前.c文件中被任意其他函数调用,并且在程序结束时才会释放
const:
const就是表示不可改变
它始终修饰const后面的第一个符号或变量
(1)
char const *p = &a;
const char *p = &a;
表示 p 上的数值不能够通过p改变,a本身是可以改的
(2)
char * const k = &a;
这里表示k指针只能够指向a的地址
static 修饰的静态局部变量,仅仅只在当前函数有效
static 修饰的全局变量,在"当前.c文件"中的任意一个函数中都有效- 28 -
12.复杂类型定义
1.结构体
为了将多个不同类型的变量统一定义
关键系 struct
结构体变量名使用的是 .
struct computer A
A.name
结构体指针使用的 ->
struct computer *p:
p->name;
结构体尺寸:
(1)遵循当前系统字长对齐
(2)对齐当前结构体中最大的数据类型
---如果最大的数据类型比系统字长要大,那么对齐系统的字长
---如果最大的数据类型比系统字长要小,那么对齐数据类型
2.联合体
为了将多个不同的选项放在一起,每次只能选择一个
关键字 union
联合体的尺寸,就是该联合体中内存最大的成员所占的内存
联合体的变量,都是从同一个地址上开始赋值的。
当你赋值完一个以后,其他的变量也就是被赋值了
联合体尺寸同样遵循结构体尺寸的特点
(1)遵循当前系统字长对齐
(2)对齐当前结构体中最大的数据类型
---如果最大的数据类型比系统字长要大,那么对齐系统的字长
---如果最大的数据类型比系统字长要小,那么对齐数据类型- 29 -
题目:
判断一台计算机是大端存储还是小端存储
大端存储:高位放低地址,低位放高地址
小端存储:低位放低地址,高位放高地址
3.枚举
为了让数值按自然数自动增加。
枚举的数值是常量。
默认后一个数值比前一个数值大1,除非在定义时直接赋值
关键字 enum
总结复合类型:
(1)
结构体是每一个成员都占用内存
联合体是所有成员共用同一块内存
枚举是成员都在常量区
(2)
结构体和联合体都是遵循字节对齐和最大数据类型对齐
(3)
结构体中每一个成员都可以单独赋值,相互间不是受影响
联合体中每一个成员赋值后都会影响到其他成员的数值
(4)
枚举的数值一旦定义,不可修改
4.typedef和可移植数据类型
typedef 取别名
--->用来给数据类型起额外的名字,
改名字在当前程序中有效
练习:
使用typedef定义一个学生结构体,包含学生的姓名,年级,性别
分别用三种不同的方式写入以下数据
{"小明",3,男}
{"小红",4,女}
{"小刚",5,男}
5.在函数中对结构体进行赋值
见代码test.c
重点:
结构体的定义,赋值,变量与指针的取值方式- 30 -
13.宏定义
关键字 define
宏定义定义的不是变量,而是一个符号
在预处理过程中就被替换掉,所以它不会参与程序中的运算
宏定义也可以设置成传参的形式,但注意以下几点:
(1)符号一定要加(),防止替换以后优先级被改变
(2)严禁自加,自减运算
当函数特别短小并且调用次数特别多的时候,使用宏定义
当宏定义需要多行表示的时候,添加换行符 ''
当宏定义表示函数时,要使用 ({ ..代码.. })
14.条件编译
关键字 #ifdef #else #endif //用是否定义来判断
与定义的数值是否为0无关
#if #elif #endif //用数值的大小判断
判断非0状态
主要使用来调试BUG
在gcc编译器中,有参数"-D"来表示外部宏定义
未符号要求的代码,会在预处理过程中被删除掉
15.多文件编译
多个.c文件同时编译生成一个可执行程序
目的
(1)让一个.c文件的内容不要太长
(2)让不同的函数存在一定的作用域
每一个.c文件都是独立,都需要头文件
往往会自定义一个统一的头文件- 31 -
.h文件标准写法
#ifndef 文件名称
#define 文件名称
......
#endif
跨文件的变量共用
关键字 extern
(1)在.h文件中包含extern的关键字声明,并且不可赋值
(2)在任意一个.c文件中直接定义该变量,此时可以赋值,只能初始化定义一次
(3)extern修饰函数,一般情况下,省略extern,只能放在.h文件中
仅限于当前文件使用
关键字 static
(1)在修饰全局变量以后,该变量只能在当前.c文件中使用
(2)在修饰函数以后,该函数只能在当前.c文件中使用
"-Wall"表示开启所有警告,严格编译
报错:
multiple definition --->重复定义
思考:
如果多个.c文件同时用到了相同的结构体,应该放在哪里
#include <...> 表示在环境变量中的头文件变量查找.h文件
linux中的头文件变量是文件夹 /usr/include
#include "..." 表示在当前文件夹内查找该头文件
16.递归函数,回调函数
递归函数:
自己调用自己
练习:
1! + 2! +3! + .... +10!
n!= 123...(n-1)*n
练习:
在ubuntu上通过键盘输入,做2048- 32 -
1.递归函数
函数会自己调用自己
(1)有穷性
递归函数中一定会有一个结束的标志
(2)多义性
一定会有判断语句,并且至少有2中,一种是调用自己,一种是结束
假设有迷宫,空格是路,*表示墙壁,要求从左上角走到右下角,求路线
* * * *
*
2.回调函数
定义:可以在不理会原函数运行的情况,直接调用它
本质:将指针函数当做函数的参数使用
但在这之前,必须定义一个函数类型表示该函数,方便传参
回到函数在使用的时候,不必在乎需要执行的是什么内容,只需要
在参数的选择
存在字符串处理函数
char *strcpy(char *dest, const char *src);
char *strcat(char *dest, const char *src);
要求使用一个函数来表示这两个功能
=============2048=================
练习1:
存在一维数组a[4],通过键盘向数组中输入四个数值
(1)如果数组中有相同的数值且中间没有除0以外的数值,
则合并两个数值,并且删除高位的数值,将这一位赋值为0
(2)如果输入的数值低位中有0,则处理掉,并且后面的数值往前移动,
原来的位置写0,直到没有数值可以移动结束- 33 -
例如:
0,1,1,0 --->0,2,0,0 --->2,0,0,0
1,1,3,0 --->2,0,3,0 --->2,3,0,0
2,4,2,0 --->不能合并
重点:
递归函数
<-------------------------------------------------------------------------------->

posted on 2020-08-26 15:12  Qianer  阅读(100)  评论(0)    收藏  举报

导航