目录
Linux命令
linux中一切皆文件
两大特殊文件:
根目录:/
家目录: ~
快捷键
放大终端:Ctrl shift +
缩小终端:Ctrl -
清屏: Ctrl L
用户名: whoami
主机名: hostname
:$中间(~):当前路径
默认在家目录 pwd
pwd:查看当前路径
新建
文件夹: mkdir 文件夹名字
文件: touch 文件名.后缀
多层嵌套:文件夹
mkdir -p 文件夹名字/名字/名字 mkdir -p a/b/c
同一路径下:
文件夹:不能创建同名文件夹,会报错
文件:更新时间戳
查看
ls 查看当前路径下所有内容
ls -a 查看当前路径下所有内容,包括隐藏内容
ls -l 查看当前路径下所有内容de详细信息
文件类型:7种 bcd-lsp
b:块设备文件 /dev
c:字符设备文件 /dev/input
d:目录文件 文件夹
-:普通文件 .c .h .txt
l:软链接文件 <linuxC高级>
s:套接字文件 <网络编程>
p:管道文件 <IO>
文件权限
rwx: r:读 w:写 x:执行
rwx rwx rwx
自己 组内 其他
r:4 w:2 x:1 -:0
路径cd
下一级:cd 文件夹名字
上一级:cd ..
当前: cd .
上一次:cd -
根目录:cd /
家目录:cd ~ 或 cd 或 cd /home/hq
绝对路径:从根目录开始 /home/hq/24081/1
相对路径:以当前位置为参照物
复制cp
文件 cp 要复制的文件名 目标地址(文件夹)
文件夹 cp -r 要复制的文件夹名 目标地址(文件夹)
另存为 cp 要复制的文件名 目标地址/新名字
移动mv
mv 要移动的文件/文件夹 目标地址
重命名: mv 要移动的文件/文件夹 目标地址/新名字
删除rm
删除后,文件不可恢复
文件: rm 文件名
文件夹: rm -r 文件夹
-f 强制
打开终端
1. 直接点击
2. Ctrl shift n 当前文件夹
3. Ctrl alt t 家目录
关闭终端
1. 直接点击
2. Ctrl d (快捷方式)
3. exit (命令)
vi编辑器
打开:vi 1.c 初始默认在命令行模式
vi编辑器3种模式:
命令行模式:复制、粘贴、撤销、光标移动
插入模式:编写代码
底行模式:保存、退出、复制、分屏、查找、替换
简单编程步骤:
1. 创建文件 touch file.c
2. 打开vi编辑器 vi file.c 默认在命令行模式,需要切换到插入模式
3. 编写代码
4. 进入底行模式 w保存
5. 运行程序: gcc file1.c 编译c文件,生成可执行文件
6. 运行: ./a.out
命令行:
复制 yy nyy(光标选中y n:行数 复制几行)
删除(剪切) dd ndd
粘贴 p
撤销 u
反撤 ctrl r
光标移动首行 gg
光标移动最后一行首行 G
光标移动行首 0
光标移动行尾 $
插入模式 i o a I O A
底行模式:
指定行复制 5,10y 复制5-10行
指定行删除 5,10d
保存 w
退出 q
保存并退出 wq
强制 !
vsp 分屏本文件
vsp 文件名 分屏打开另外一个文件
查找 /找的东西
替换 s/a1/a2 光标所在行的第一个a1替换成a2
s/a1/a2/g 光标所在行的所有a1替换成a2
%s/a1/a2/g 改变全文
1,$s/a1/a2/g 改变全文
设置行号 set nu
取消 set nonu
取消高亮 nohl
vscode编辑器
建议打开整个文件夹
code 文件夹名字
gcc编译器
高级语言(C语言)---机器识别不了,借助编译器
简写:
gcc file2.c 生成可执行文件名a.out
./a.out
另一种简写:
gcc file2.c -o file2(可执行文件名,可自己命名)
./file2
完整编译步骤:
预处理、编译、汇编、链接
1. 预处理
展开头文件、删除注释,替换宏定义
gcc -E hello.c -o hello.i
2. 编译
检查语法错误,有错报错,没错会转变成汇编语言,生成汇编文件
gcc -S hello.i -o hello.s
3. 汇编
把汇编文件生成不可执行的二进制文件
gcc -c hello.s -o hello.o
4. 链接
链接库文件,最终生成机器能够识别的二进制的可执行文件
gcc hello.o -o hello(可执行文件,可以自己取名)
C基础
计算机存储单位:位 0 1
1字节byte=8位bit
换算:
1B=8bit
1KB=1024B
1MB=1024KB
1GB=1024MB
1TB=1024GB
计算机数据类型
二进制 0 1 10 (1/0组合排列)
八进制 0-7 010(在最前方加0)
十进制 0-9
十六进制 0-9 a-f 0X10 (在最前面加0x)
a | b | c | d | e | f |
10 | 11 | 12 | 13 | 14 | 15 |
进制转换
十进制转二进制:
短除法:除以二取余,余数自下而上排列 12:1100
拆分法:8421
二进制转八进制:
三个二进制数转换为一个八进制数
二进制转十六进制:
四个二进制数转换为一个十六进制数
非数值型数据
字符型 用 ' ' 包裹
man man :查看man手册
man手册:用来查看一些命令、函数的用法
1 可执行程序或 shell 命令
2 系统调用(内核提供的函数)
3 库调用(程序库中的函数)
man ascii 查看ASCII码表
'\0' 0 字符串结束标志
'\n' 10 换行
空格 32
'0' 48 字符0
'A' 65
'a' 97
字符转数字 '2'-48==2
'A' +32 = 'a'
'A' +' ' = 'a'
词法符号
关键字:32
存储类型:auto static extern register
数据类型:int short long c har float double signed unsigned
分支:if else switch case default
循环:for do while go to break continue
构造数据类型:struct enum union
其他:void const sizeof typedef return volatile
虚拟内存空间:
32位:4G
64位:2^64 2^48
栈区:系统自动开辟、回收 (函数体内部定义的变量)
堆区:程序员手动开辟(malloc)、回收(free)
全局区、静态区:
常量区:char *p="hello"
标识符
定义变量名、数组名、函数名时,遵循标识符命名规则:
1. 由字母、数字、下划线(_)组成
2. 开头不能是数字
3. 不能和关键字重名
4. 见名知意
运算符
算术运算符 逻辑运算符 关系运算符 位运算符 三目运算符 赋值运算符
算术运算符
+ - * / % ++ --
/:如果是整数相除,结果向下取整
int a=5/2;
printf("%d\n",a); //2 %d整型 int
float a=5/2;
printf("%f\n",a); //2.000000 %f浮点型 float 默认打印小数点后六位
float a=5.0/2;
printf("%f\n",a); //2.500000
printf("%.1f\n",a); //2.5 .1保留一位小数
float a=(float)5/2; // int 5短暂的强制转换成float float 5只在当前这一行
printf("%.1f\n",a); //2.5
%取余
只能用于整数运算
5%2==1
++
a++ <==> a=a+1
独立成句:
int a=5;
1) ++a;
printf("%d\n",a); //6
2) a++;
printf("%d\n",a); //6
赋值运算:
int a=5;
1)int b= a++; // = +
printf("%d %d\n",a,b);//6 5
2)int b= ++a; // + =
printf("%d %d\n",a,b);//6 6
打印语句
int a=5;
1)printf("%d\n",a++); //5
printf("%d\n",a); //6
2)printf("%d\n",++a); //6
printf("%d\n",a); //6
运算符
1.1逻辑运算符
与 :&& 全真则真,有假则假;
或 :|| 一真则真,全假则假;
非 :! 非真即假,非假即真;
判断是否成立,输出结果为0(否)或1(是);
终端验证条件是否成立:
printf("%d\n",118>99); // 条件成立输出 1
printf("%d\n",18>99); // 条件不成立输出 0
printf("%d\n",118>99 && 5>9 ); // 输出 0
printf("%d\n",118>99 || 5>9 ); // 输出 1
printf("%d\n",!(118>99) || 5>9 ); // 输出 0
0为假,非零为真
printf("%d\n",5&&6); // 1
printf("%d\n",(10/2)&&6); // 1
printf("%d\n",(10/2==5)&&6); // 1
截断法则
逻辑与运算中,前边表达式结果为假,后边表达式不再执行,返回假;
逻辑或运算中,前边表达式结果为真,后边表达式不再执行,返回真;
例:
解析:
(m=a>b)为假,后面表达式不再执行,则n=2;
1.2位运算符
位与:& 位或: | 异或: ^ 取反:~ 左移: << 右移: >>
& 位与:全1则1,有0则0
| 位或:有1则1,全0则0
^ 异或:相同为0,不同为1
int a=6,b=10; 将6和10换算为二进制数:
int c=a&b //6 : 0110
↓↓↓↓ 按位比较
printf("%d\n",c);//输出2 //10:1010 全1则1,有0则零
↓↓↓↓
//&: 0010换算为十进制数→→2
负数运算
整数以补码的二进制进行存储、计算
原码 | 反码 | 补码 | |
正数 | 相同 | 相同 | 相同 |
负数 | 相同 | 原码除符号位之外,其他按位取反 | 反码+1 |
int a=-6,b=10;
int c=a&b;
printf("%d\n",c); //10
int 4字节 = 32位
-6原码: 最左边表示符号位 0正数 1负数
1000 ... 0110
-6反码:
1111 ... 1001
-6补码:
1111 ... 1010
10补码:
0000 ... 1010
-6补码 & 10补码:
1111 ... 1010
0000 ... 1010
0000 ... 1010 补码 10
~ 取反:
1-->0 0-->1
int a=-6;
int c=~a;
printf("%d\n",c); //5
-6原码: 最左边表示符号位 0正数 1负数
1000 ... 0110
-6反码:
1111 ... 1001
-6补码:
1111 ... 1010
~ 取反:
0000 ... 0101 === 5
<<左移
左移几位,右边补几个0
int a=6<<2; //6转成二进制→110,再左移两位→11000
printf("%d\n",a); //11000===24
右移两位也可这么计算:
6*2^2=24
-6*2^2=-24
X右移n位,即:结果=X*2^n
>>右移
右移几位,右边丢弃几位
6>>2 //6转成二进制→110,再右移→001
6/2^2=1
1.3关系预算符
大于:> 大于等于: >= 小于:< 小于等于:<=
等于:== 不等于:!=
1.4 三目运算符 (条件运算符)
表达式1 ? 表达式2 : 表达式3;
int a=3,b=5;
int max=a>b?a:b; 判断表达式1是否成立,成立执行表达式2,不成立执行表达式3;
printf("%d\n",max); //5
1.5 赋值运算符
赋值:= 加等:+= 减等:-= 乘等:*=
除等:/= 取余等:%=
a-=5 <===> a=a-5;
扩展:逗号运算符(顺序求值运算符)
(表达式1,表达式2,表达式3,...,表达式n)
运算结果是最后一个表达式的结果
int a=3,b=5; 运算结果是最后一个表达式结果:
a=(a*3,a+2,a-1); a = a-1 = 2
printf("%d\n",a); //2
优先级
单算移关与 异或逻条赋
单目运算符:++ -- ~ !
算术运算符: / * % + -
移位运算符: << >>
关系运算符:> >= < <= == !=
位与运算符:&
异或运算符:^
位或运算符:|
逻辑运算符:&& ||
条件运算符:? :
赋值运算符: = ......
分隔符
空格 tab 换行
变量
程序运行过程中会发生变化的量
格式:
存储类型 数据类型 变量名
(auto) int a;
存储类型: 决定变量存储的位置
数据类型 :决定开辟空间大小
变量名 : 遵循标识符命名规则
名称 | 大小(字节) | 取值范围(了解) | |
int | 整型 | 4 | |
char | 字符型 | 1 | -2^7~2^7-1 |
short | 短整型 | 2 | |
long | 长整型 | 32位:4;64位:8 | |
float | 单精度浮点型 | 4 | 有效位数:6-7位 |
double | 双精度浮点型 | 8 | 15-16位 |
局部变量和全局变量的区别
局部变量 | 全局变量 | |
定义位置 | 函数体内部 | 函数体外边 |
初值 | 未初始化,值为随机值 | 未初始化,值为0 |
作用域 | 当前函数体内部 | 整个程序 |
生命周期 | 同当前函数共存亡 | 同整个函数体共存亡 |
存储位置 | 栈区 | 全局区 |
32: 2^32字节==4G
64: 2^64 2^484
栈区: 系统自动开辟、回收 (函数体内部定义的变量)
堆区: 程序员手动开辟(malloc)、回收(free)
全局区、 静态区: 全局变量、static修饰的变量
常量区: char *p="hello"
常量
数值型常量
八进制 十进制 十六进制
浮点型常量
float double
字符常量
程序中用‘ ’包裹
char a='q';
char b='\n'; \n换行
char c='n'; n字符
转义字符:\
char a='A';
printf("%c\n",a); //A
char b='123';
printf("%c\n",b); //'3'
char c=0101;
printf("%c\n",c); //A
char d='\101';
printf("%c\n",d); //A
char e='\x41';
printf("%c\n",e); //A
字符串常量
结束标志:用" "包裹 '\0'字符串
例: "hello" 5个字符+'\0'字符串结束标志 == 6个字符
标识常量
宏定义
格式:#define 宏名 常量值 或 表达式 或 函数
宏名:一般用大写,目的跟变量区分开
特点:先进行单纯的替换,替换完之后再计算
#define N 2 // N=2
#define M N+3 // M=N+3=2+3
#define NUM N+M/2+1 // NUM=N+M/2+1
void main() ↓
{ = 2+2+3/2+1
int a = NUM;
printf("%d\n",a); //
}
宏定义实现求最大值
#include<stdio.h>
#define MAX a>b?a:b // 定义MAX 为 a>b?a:b
int main()
{
int a=6,b=9;
printf("%d\n",MAX);
return 0;
}
输入输出
按字符输入输出
getchar : 输入
语法格式:int getchar(void);
功能:终端输入一个字符
参数:无
返回值:输入字符的ASCII码值
int a = getchar();
printf("%d\n",a);
putchar : 输出
语法格式:int putchar(int c);
功能:终端输出一个字符
参数:字符的ASCII码值
返回值:字符的ASCII码值
按格式
int a=getchar();
putchar(a);
putchar('\n'); // 或 putchar(10); \n 的ascii码为10
输入输出
scanf : 输入
语法格式:int scanf(const char *format, ...);
功能:按格式向终端输入
参数:多个
返回值:成功输入的元素个数
int a;
scanf ("%d",&a);
printf ("%d\n",a);
printf : 输出
语法格式: int printf(const char *format, ...);
功能:按格式终端输出
格式:
%d int
%c char
%f float %.nf 保留n位小数
%lf double
%s 字符串
%p 指针
%o %#o 八进制
%x %#x 十六进制
int a=1;
char b='B';
float c=2.000;
printf("%d %c %f\n",a,b,c); // 输出的格式要跟元素格式相对应
//输出结果为:1 B 2.000000
参数:多个参数
返回值:输出元素的个数
int a=011; // 定义a为八进制数11,0开头所以为八进制数
printf("%o\n",a); // 11
printf("%#o\n",a); // 011
位宽
printf("%3d\n",a) // 可以改变d前的数字,改变输出的位宽
int a = 6;
b = 66;
c = 666;
printf("%3d\n",a); //如果d前数字小于输出数据的位数,则无法改变位宽
printf("%3d\n",b);
printf("%3d\n",c);
输出结果为:
6
66
666
垃圾字符回收
1.通过一个空格回收一个或多个空格、tab
2.%*c *抑制符 回收任意一个字符
3.getchar
分支语句
if
基本格式
if(表达式)
{
语句块1;
}else
{
语句块2;
}
//运行顺序:先判断括号内表达式是否成立 成立则执行语句块1 反之执行2
int age=20;
if(age>18)
{
printf("work\n");
}else
{
printf("study\n");
}
//20>18,执行第一语句块printf("work\n"); 输出work
分层结构
if(表达式1)
{
语句块1;
}else if(表达式2) //表达式1成立,执行语句块1,反之执行第二个if语句;
{
语句块2;
}else
{
语句块n;
}
===============================
100-90 A
89-80 B
79-70 C
<70 补考
int s=0;
scanf("%d",&s);
if(s>=90 && s<=100)
{
printf("A\n");
}else if(s>=80 && s<=89)
{
printf("B\n");
}else if(s>=70 && s<=79)
{
printf("C\n");
}else
{
printf("补考\n");
}
嵌套结构
if(表达式1)
{
if(){}; //表达式1成立,执行if(){},反之执行else后语句块1
}else
{
语句块1;
}
======================
if(s>=0 && s<=100)
{
if(){};
}else
{
printf("error\n");
}
注意:
else前边必须有if; if后边可以没有else
当分支语句中,语句块只有一行时可以省略{ }.
switch
switch (表达式)
{
case 常量值1:
/* code */ //表达式结果分别与每个常量值比较,相等输出对应常量值后的程序
break;
case 常量值2:
/* code */
break;
default:
/* code */
break;
}
==========================
case后的常量值只能是数字或字符
循环语句
for
for(表达式1;表达式2;表达式3)
{
语句块;
}
表达式1:循环变量赋初值
表达式2:终止条件
表达式3:增值或减值
================================
for (int i = 0; i < 5; i++)
{
printf("%d\n",i);
}
双层for
for(int i=1;i<6;i++)
{
for(int j=1;j<=4;j++)
{
printf("i:%d j:%d\n",i,j);
}
}
=========================================
for(int i=1;i<10;i++)
{
for(int j=1;j<=i;j++)
{
// printf("%d*%d=%2d ",i,j,i*j);
printf("%d*%d=%d\t",i,j,i*j);
}
putchar(10);
}
while
int i=1;
while(i<5)
{
printf("%d\n",i); // 1<5表达式成立,执行内部程序
i++;
}
while
int i=1;
while(i<5) //当i<5时,执行{}内语句块,直到不满足i<5;
{
printf("%d\n",i);
i++;
}
============================
int i=2,sum=0;
while(i<=6)
{
if(i%2==0)
{
sum+=i;
}
i++;
}
printf("%d\n",sum);
while (1) 死循环;
do...while
do...while
int i=12;
do
{
printf("%d ",i);
i++;
} while (i<=6);
//do while :先执行{}内语句块,再判断while后表达式;
//while ; 先判断while后表达式,再执行{}内语句块;
while(i<=6)
{
printf("%d ",i);
i++;
}
// 单行注释 Ctrl /
/**/ 多行注释 选中一片代码 ctrl shift a 不支持嵌套多行注释
while 先判断,后执行
do...while 先执行,后判断
循环控制语句
break 结束整个循环
continue 结束本次循环,继续下一次循环
return main函数中,结束整个程序
for (int i = 1; i < 7; i++)
{
if (i == 3)
{
break; //结束整个循环;
continue; //跳过本次循环,进入下次循环;
return 0; //结束整个程序;
}
printf("i:%d ", i);
}
printf("qqqqq\n");
数组
由一个或多个相同类型数据组成的集合
特点:
数据类型相同
内存连续
格式:
存储类型 数据类型 数组名[元素个数]
int a[5];
只有在定义数组时,[ ] 里表示元素个数,其他情况下,[ ] 里表示索引
例:
int a[5]={2,3,4,5,6};
printf("%d %d %d\n",a[0],a[2],a[4]); //2 4 6
访问元素:数组名[索引] //索引从0开始
数据类型保持一致
int a[5]={2,3,4,5,6};
char b[5]={'q','w','e'};
数组名
数组名也是数组首地址,是一个地址常量,不能被修改
int a[5]={2,3,4,5,6};
printf("%d %d %d\n",a[0],a[2],a[4]);
printf("%p\n",a); //数组名字,就是数组的首地址;
printf("%p\n",&a[0]);
数组大小
int a[5]={2,3,4,5,6}; //20
int a2[5]={2,3}; //20
int a3[5]={2}; //20 //int数据类型,大小为4字节,元素个数×数据类型大小
int b[]={2,3,4,5,6}; //20
int b2[]={2,3}; //8
int b3[]={2}; //4
printf("%ld\n",sizeof(a));
元素个数*数据类型大小 5*4=20
初始化
● 完全初始化
int a[5]={2,3,4,5,6};
● 部分初始化
int a[5]={2,3};
未初始化部分,值默认为0
int a[5]={}; //0 0 0 0 0
int a[5]={0}; //0 0 0 0 0
● 未初始化
int a[5];
值为随机值
可单独赋值a[0]=2;
int a[5]={};
for(int i=0;i<5;i++)
{
scanf("%d",&a[i]);
}
for(int i=0;i<5;i++)
{
printf("%d\n",a[i]);
}
int a[5]={9,8,7,6,5};
for(int i=0;i<5;i++)
{
printf("%p\n",&a[i]); //循环打印a[5]每个元素
} // 输出: 9 8 7 6 5
清零函数
bzero
#include <strings.h>
void bzero(void *s, size_t n);
功能:数组清零
参数: s:数组首地址 n:字节大小 size_t==int
返回值:无
int a[5]={};
for(int i=0;i<5;i++)
scanf("%d",&a[i]);
for(int i=0;i<5;i++)
printf("%d\n",a[i]);
bzero(a,sizeof(a));
for(int i=0;i<5;i++)
printf("%d\n",a[i]);
memset
#include <string.h>
void *memset(void *s, int c, size_t n);
功能:数组清零
参数: s:数组首地址 c:要设置的值 n:字节大小
返回值:数组首地址
int a[5]={};
for(int i=0;i<5;i++)
scanf("%d",&a[i]); //循环输入;
for(int i=0;i<5;i++)
printf("%d\n",a[i]); //循环打印,遍历a[5]内容;
memset(a,0,sizeof(a)); //清零
for(int i=0;i<5;i++)
printf("%d\n",a[i]);
按字节赋值 int 4字节 1字节=8位 00000001 00000001 00000001 00000001
字符数组
存放字符串
char a[]={'n','i','c','e'}; //4 4个单个字符,4×1=4;
char b[5]={"nice"}; //5 定义数组元素为5个,5×1=5;
char c[33]="nice"; //33 定义数组元素为33个,33×1=33;
char d[]="nice"; //5 字符串最后有'\0'结束标志,也算作一个字符 5×1=5;
字符数组输入输出
1. for
for(int i=0;i<5;i++)
scanf("%c",&ch[i]); //循环向ch[5]输入字符;
for(int i=0;i<5;i++)
printf("%c",ch[i]); //循环打印,遍历ch[5]的内容;
2. %s
char ch[33]={0};
scanf("%s",ch);
printf("%s\n",ch);
//scanf 默认遇到空格或回车赋值结束,如果想保留空格,可以:
scanf("%[^\n]",ch);
3. gets puts
[1] 计算字符串实际元素个数
char a[33]="hello world";
int i;
for(i=0;a[i]!='\0';i++);
printf("%d\n",i);//11
strlen
#include <string.h>
size_t strlen(const char *s);
功能:计算字符串实际元素个数 不包括'\0'
参数:字符数组首地址
返回值:字符串实际元素个数
int num=strlen(a);
printf("%d\n",num);//5
或
printf("%d\n",strlen(a));//5
sizeof和strlen的区别:
1. sizeof是关键字,strlen是函数
2. sizeof计算元素实际开辟的空间大小,strlen计算字符串实际元素个数;sizeof计算包括'\0',strlen不包括'\0'
3. char a[ ]="hello"; sizeof(a)==6 strlen(a)==5
判断:
char s[10]="hello";
以下几种赋值方式:
1) s[10]="hello";
//只有在定义数组时,[ ]里表示元素个数,其他情况下,[ ]里表示索引
2) s="hello";
//数组名也是数组首地址,是一个地址常量,不能被修改
3) strcpy(s,"world");
冒泡排序
//从小到大排 确保右边是大的值
int a[5]={6,5,4,3,2},temp;
for(int i=0;i<5-1;i++) //轮数 5个数比较4轮
{
for(int j=0;j<5-1-i;j++) //每一轮比较的次数
{
if(a[j]>a[j+1]) //确定了最大或最小值之后,这个值被固定到最右边,之后不参与比较,所以随着循环的执行,参与比较的元素越来越少,它的索引越来越低
{
temp=a[j];
a[j]=a[j+1];
a[j+1]=temp;
}
}
}
for(int i=0;i<5;i++)
printf("%d ",a[i]);
选择排序
6 5 4 3 2
int a[5]={6,5,4,3,2},m,temp;
for(int i=0;i<5-1;i++) //轮数
{
m=i; //m:最小元素下标 ,默认是参与比较的元素中最左边位置
for(int j=i+1;j<5;j++) //获取剩下的所有元素
{
if(a[m]>a[j])
{
m=j; //在循环内部,暂存最小元素下标
}
} //一轮循环结束,交换位置
//交换
if(m!=i) //当最小元素下标==要交换位置的下标,不需要交换
{
temp=a[m];
a[m]=a[i];
a[i]=temp;
}
}
for(int i=0;i<5;i++)
printf("%d ",a[i]);
二维数组
格式:
数据类型 数组名[行数][列数];
int a[2][3]={2,3,4,5,6,7};
访问: 数组名[行下标][列下标] 下标从0开始
a[0][0] | a[0][1] | a[0][2] |
a[1][0] | a[1][1] | a[1][2] |
int a[2][3]={2,3,4,5};
printf("%d %d\n",a[0][0],a[1][0]);
定义二维数组时,可以省略行数,不能省略列数
int a[2][ ]={2,3,4,5,6,7}; //错误 无法确定具体两行几列
int a[ ][3]={2,3,4,5,6,7};
D
数组名
是第一行的首地址,是个行地址
a: 第一行首地址
a+1:第二行首地址
int a[2][3]={2,3,4,5,6,7};
printf("%p %p\n",a,&a[0][0]);
printf("%p %p\n",a+1,&a[1][0]);
数组元素个数:行数*列数
数组大小:
行数*列数*数据类型大小
sizeof(数组名)
printf("%d\n",sizeof(a));
初始化
● 完全初始化
int a[2][3]={2,3,4,5,6,7};
int arr[2][3]={{2,3,4},{5,6,7}}; //按行赋值
● 部分初始化
未初始化部分,值为0
int a[2][3]={2,3}; // 2 3 0 0 0 0
int arr[2][3]={{2},{5,6,7}}; // 2 0 0 5 6 7
● 未初始化 值为随机值 需要单独赋值
int a[2][3];
内存分配
int a[2][3]={2,3,4,5,6,7};
a[0]:第一行第一列的地址
a[0]+1:第一行第二列的地址
地址 | 元素 | |||
a[0] | &a[0][0] | a | 2 | a[0][0] |
a[0]+1 | &a[0][1] | 3 | a[0][1] | |
a[0]+2 | &a[0][2] | 4 | a[0][2] | |
a[1] | &a[1][0] | a+1 | 5 | a[1][0] |
a[1]+1 | &a[1][1] | 6 | a[1][1] | |
a[1]+2 | &a[1][2] | 7 | a[1][2] |
int a[2][3]={2,3,4,5,6,7};
printf("%p %p\n",a,&a[0][0]);
printf("%p %p\n",a+1,&a[1][0]);
printf("%d\n",sizeof(a));
printf("%p\n",a[0]);
printf("%p\n",a[0]+1);
遍历数组
int a[2][3]={};
for(int i=0;i<2;i++)
{
for(int j=0;j<3;j++)
scanf("%d",&a[i][j]);
}
for(int i=0;i<2;i++)
{
for(int j=0;j<3;j++)
printf("%d\n",a[i][j]);
}
指针
一级指针存放普通变量的地址
格式:
存储类型 数据类型 * 指针变量名
int *p;
定义指针
只有在定义指针时,* 表示定义指针的标志,其他情况下 * 表示取内容
例:
int a=10;
int *p=&a; // p 内储存变量 a 的地址
定义指针时,一定要对它初始化,否则就会产生野指针
1)int *p; // p为野指针,没有初始化,是个随机的地址;
2)int *p=NULL; // 指针初始化,可以赋值为NULL(空地址);后边可以再对p重新赋值
int b=9;
p=&b;
操作符
* :取内容符 取指针指向的地址内的内容
& :取地址符 取变量的地址
int a=10;
int *p=&a;
printf("%d\n",*p); //输出10
=====================
地址: &a == p
内容: a == *p
初始化
1.存放普通变量的地址
1.int *p=&a; 定义的同时赋值;
2.int *p=NULL;
p=&a; 先定义p为空指针,再赋值;
2.存放数组的首地址
int arr[5]={5,6,7,8,9};
int *q=arr; // q内存的是数组首地址
===============================
char s[33]="hello"; //s:数组首地址
char *p=s; // 数组首地址赋值
printf("%p %p\n",s,p); // s,p都是数组首地址
printf("%s\n",p); //"hello",p内存的数组首地址,打印字符串
printf("%c\n",*p); //'h',p内存的数组首地址,打印第一个字符
3.存放另一个指针变量的地址
int a=6;
int *p=&a;
int *q=p; //int *q=&a;
修改其中任意一个,另外几个的值会同步修改
运算符
算数运算
p++:向高地址方向移动一个数据单位(int:4 char:1);指针的指向发生改变
char c[6]={"hello"};
char *p=c;
p++; //p++后,向高地址方向移动1个数据单位(char);
======================
int a[5]={5,6,7,8,9};
int *q=a;
q++; //++后,向高地址方向移动1个数据单位(int);
p+n:向高地址方向(暂时)移动n个数据单位(int:4 char:1);指针的指向不改变
char c[6]={"hello"};
char *p=c;
p++; //p++后,向高地址方向移动 n 个数据单位(char);即为数组下一元素的地址;
======================
int a[5]={5,6,7,8,9};
int *q=a;
q++; //++后,向高地址方向移动 n 个数据单位(int);即为数组下一元素的地址
同一个数组中:两个地址的差值==相差元素个数
int a[5]={5,6,7,8,9};
int *p=a;
int *q=&a[2];
printf("%d\n",q-p); //2
关系运算
< > ==
比较的是地址的高低(同一个数组中比较)
指针大小
int a[5]={5,6,7,8,9};
int *p=a;
printf("%ld\n",sizeof(p)); //8
int b=6;
int *p1=&b;
printf("%ld\n",sizeof(p1)); //8
char c='q';
char *p2=&c;
printf("%ld\n",sizeof(p2)); //8
float d=2.3;
float *p3=&d;
printf("%ld\n",sizeof(p3)); //8
64位操作系统:指针大小为8字节
32位操作系统:指针大小为4字节
指针修饰
const
更改为只读,不可修改
修饰普通变量
const int a=1; // 也可以: int const a=1;
int *p=&a; // 修饰普通变量,普通变量无法通过变量名字更改
*p=2; // 只能通过指针间接更改
修饰指针
const * p
int const *p=&a; //修饰指针指向的内容,内容不可修改,指针指向可以修改;
* const p
int * const p=&a; //修饰指针指向,指向不可修改,指针可以修改;
const * const p
int const * const p; //既修饰指针指向,也修饰指针指向的内容;
void
定义一个任意类型的指针,只能用于修饰指针,不可以修饰变量;
void * p=NULL; //定义一个任意类型的指针;
实际运用时, 需要将指针类型进行强转:
(int * )p // 取地址
*(int * )p // 取内容
(char * )p // 取地址
*(char * )p // 取内容
大小端
大端:低地址存放高字节数据,高地址存放低字节数据;
小端:低地址存放低字节数据,高地址存放高字节数据;
例如:
0x12345678
高字节 ←←←←←←←← 低字节
大端:12 34 56 78
小端:78 56 34 12
二级指针
二级指针内存放一级指针的地址
int a = 1;
int *p =&a; // p中存放变量a的地址;
int **q =&p; // q中存放指针的地址;
变量a的:地址 内容
&a a
p *p
*q **q
指针和数组
指针和一维数组
直接访问
int a[5]={1,2,3,4,5};
地址 | 元素 | |||
a | &a[0] | 1 | a[0] | *a |
a+1 | &a[1] | 2 | a[1] | *(a+1) |
a+2 | &a[2] | 3 | a[2] | *(a+2) |
a+3 | &a[3] | 4 | a[3] | *(a+3) |
a+4 | &a[4] | 5 | a[4] | *(a+4) |
间接访问
int a [5]={1,2,3,4,5};
int *p=a;
地址 | 元素 | |||
p | &p[0] | 1 | p[0] | *p |
p+1 | &p[1] | 2 | p[1] | *(p+1) |
p+2 | &p[2] | 3 | p[2] | *(p+2) |
p+3 | &p[3] | 4 | p[3] | *(p+3) |
p+4 | &p[4] | 5 | p[4] | *(p+4) |
a 和 p 不完全一样
a:地址常量,不可修改
p:指针变量,可以修改
运算:
(单目运算符,从左往右计算)
++ * p : 先 * p ,再 ++ *p;
* p ++ : 先 p++,再* p++;
* ++ p : 先++p ,再 * ++p;
( * p ) ++ : 先*p, 再 *p ++;
* ( p ++ ) : 先p ++,再 * p++;
* ( ++ p ) : 先++ p ;再* ++p
指针和二维数组
int a[2][3]={2,3,4,5,6,7};
数组名是行地址 ,一行里边有多列,行>列,如果想访问到列,需要加 * 降级处理
*a 第一行第一列
*a+1 第一行第二列
*(a+1) 第二行第一列
*(a+1)+1 第二行第二列
a 第一行首地址
a+1 第二行首地址
a[0] 第一行第一列
a[0]+1 第一行第二列
地址 | 元素 | ||||||
*a | a[0] | a | &a[0][0] | 2 | a[0][0] | *a[0] | **a |
*a+1 | a[0]+1 | &a[0][1] | 3 | a[0][1] | *(a[0]+1) | *(*a+1) | |
*a+2 | a[0]+2 | &a[0][2] | 4 | a[0][2] | *(a[0]+2) | *(*a+2) | |
*(a+1) | a[1] | a+1 | &a[1][0] | 5 | a[1][0] | *a[1] | **(a+1) |
*(a+1)+1 | a[1]+1 | &a[1][1] | 6 | a[1][1] | *(a[1]+1) | *(*(a+1)+1) | |
*(a+1)+2 | a[1]+2 | &a[1][2] | 7 | a[1][2] | *(a[1]+2) | *(*(a+1)+2) |
数组指针
指向数组的指针
格式:(*指针变量名字)[列数]
例:int a[2][3]={1,2,3,4,5,6};
int *p[4]=a;
p+1:一次移动[n]位; (p+1,想当于下一行首地址)
地址 | 元素 | ||||
p[0] | p | *p | 2 | **p | *p[0] |
p[0]+1 | *p+1 | 3 | *(*p+1) | *(p[0]+1) | |
p[0]+2 | *p+2 | 4 | *(*p+2) | *(p[0]+2) | |
p[1] | p+1 | *(p+1) | 5 | **(p+1) | *p[1] |
p[1]+1 | *(p+1)+1 | 6 | *(*(p+1)+1) | *(p[1]+1) | |
p[1]+2 | *(p+1)+2 | 7 | *(*(p+1)+2) | *(p[1]+2) |
数组指针的大小:相当于指针的大小,64位8个字节,32位4个字节
指针数组
储存指针的数组
格式:*指针变量名字 [元素个数]
例:int a=1,b=2,c=3;
int *p[3]={&a,&b,&c};
变量abc的地址: p[0]=&a, p[1]=&b, p[2]=&c;
内容:a=*p[1] b=*p[2] c=*p[3]
指针数组的大小:指针数组内元素个数*8
命令行参数
argv :指针数组,存放命令行传递的字符串
argc :命令行传递的字符串的个数