计算机基础

终端相关

查看相关知识

Kernel - the innermost part of any modern operating system which directly talks to actual hardware.

Shell - wrapper around the actual Kernel. Whenever we run command, we actually talk to shell which in turn invokes appropriate Kernel instructions. Apart from this, the shell is capable of performing some other stuffs like finding appropriate program while having commands, some file name short hand, piping commands etc.

Linux/MAC流行的shell是bash;Windows通常使用cmd shell,最初兼容ms dos而构建;power shell全新命令行shell技术可以在Windows,Linux,MAC使用;

Terminal - in the era of earlier computing, computers (known as Mainframe) were giant. So, it was easy to have a single processing unit and connect it from many places. Terminal is the actual hardware with keyboard and output devices connected to mainframe.

Console - Special type of terminal which is directly connected to Mainframe for the purpose of OS Administration.

tty - TeleTypewriter used to send and receive data to and from Mainframe. Used before Video Terminals were available. But conventionally it has been still named as tty. Even the coommand stty

Linux相关

查看相关知识

1.Linux文件系统与目录

查看相关知识
  • Linux系统目录呈现倒树型结构
  • 使用.表示当前目录,..表示上级目录
  • 路径:1.绝对路径-起点为根目录;2.相对路径-起点为当前目录
  • Windows系统路径使用\间隔,Linux系统路径使用/间隔
  • Linux主目录使用~标识

2.Linux常用命令

查看相关知识
  • clear-清除终端软件所有文字信息
  • pwd-查看当前目录的位置
  • cd 目录目录-修改当前目录位置
  • ls-用于查看目录内容,其中ls -a查看所有内容,ls -l查看每个项目的详细信息
  • touch-创建空文件
  • mkdir-创建空目录(父目录必须存在),mkdir 路径 -p逐级创建指定目录
  • rm-删除文件或目录,rm 文件名删除文件,rm -rf 目录名删除目录
  • cp-拷贝文件或目录,rm 源文件 目标文件拷贝文件,cp -rf 源目录 目标目录拷贝目录
  • cat-快速查看文件内容

3.vim编辑器

查看相关知识
  • 三种工作模式:正常模式执行简单命令,例如复制粘贴等;插入模式:实现编辑功能;命令行模式:执行复杂命令

  • 一般模式→编辑模式:输入i;一般模式→命令行模式:输入:;退回至一般模式使用esc

1
2
3
4
5
6
7
8
9
10
一般模式:
1.移动光标与撤销操作:
G:移动至文件最后一行
nG:移动至文件的第n行
u:撤销上一步操作
2.复制粘贴操作:
nyy:当前光标所在行开始的连续n行内容拷贝至剪贴板
ndd:当前光标所在行开始的连续n行内容剪切至剪贴板
p:将剪贴板内容在当前光标所在行下面粘贴
x:删除所在光标位置后的一个字符
1
2
3
4
命令行模式:
1.w保存所有修改但不退出vim编辑器
2.wq保存并退出
3.q!放弃保存并退出

C语言相关

查看相关知识

一、历史背景

编程语言演变过程:机器语言(纯粹二进制)→汇编语言(本质是助记符语言,受平台约束)→高级语言(C语言,C++等)

二、语法

1.案例:

查看相关知识
1
2
3
4
5
6
7
8
9
 /*这是我的第一个程序,很开心,文件名hello.c*/
//头文件包含指令,预处理阶段会将头文件拷贝替换,使用<>至系统目录查找适用的头文件,使用""先至当前目录查找,再去系统目录查找
#include<stdio.h>
//c语言程序有且仅有一个main函数,程序从main开始从main结束
int main(void)//函数返回值为整型,函数形式参数为空
{
printf("hello,world\n");//C语言程序使用分号结束一条语句
return 0;//函数返回值,return关键字的主要用途为结束函数的执行,辅助用途为指定返回值的数值
}
1
2
3
4
#编译程序,计算机只能识别二进制内容,因此要将c语言程序转换成可以识别的二进制,整个转换过程称为编译,转换过程需要用的工具称为编译器,gcc是Linux系统最常用的c语言编译器
gcc hello.c
#执行当前目录下的a.out可执行程序
./a.out

编译程序的详细步骤:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
源文件→预处理→编译→汇编→链接→可执行文件
1.预处理
对源文件以`#`开头的预处理命令进行处理
gcc -E hello.c -o hello.i
-E只对源文件执行预处理步骤,-o指定生成的文件名
2.编译
对预处理后得到的xxx.i文件进行编译,生成汇编语言文件xxx.s
gcc -S hello.i -o hello.s
-S对文件进行编译操作
3.汇编
将xxx.s文件翻译为二进制语言指令,将这些指令打包成为目标文件的文件,后缀为xxx.o,是一种二进制文件
gcc -c hello.s -o hello.o
-c只进行汇编操作
4.链接
将多个目标文件和标准库函数合并成为一个可执行目标文件
gcc hello.o -o hello
案例中将hello.o和printf函数对应的目标文件printf.o合并生成可执行文件hello

编译的最好方法

1
gcc hello.c -o hello

2.printf函数

查看相关知识

(1)使用格式

1
printf("输出内容",...);

(2)可以在双引号中使用占用符

1
printf("%d",500);

3.变量

查看相关知识
  • 计算机的内存由大量的字节构成,每个字节都有一个编号,不同字节的编号不同,这个编号叫做字节的地址,所有字节的地址都是连续的,从0开始向正数方向扩展;
  • 可以将相邻的几个字节合并成一个整体用来记录一个数字,内存里用来记录一个数字的所有字节叫做一个存储区;
  • 存储区的地址为它所包含的字节里第一个字节的地址;
  • c语言程序利用变量表示存储区,变量的定义让计算机为程序分配存储区;
  • 语法格式:数据类型 变量名=变量值;
  • 变量名既代表存储区,也代表数字,由环境决定;
  • 存储区的地址也可以用于代表存储区,&变量名—变量名表示的存储区首地址,可以在printf函数调用语句里面使用%p作为占位符把地址数据显示在屏幕上;
  • 64位操作系统内存地址是8字节-64位的,32位操作系统内存地址是4字节-32位的;
  • 变量命名规则:不能以数字开头,只能以字母或者下划线;c语言关键字不能作为变量名称;大小写敏感;

4.数据类型

查看相关知识
数据类型含义占位符
char1字节(字符在内存中存储,存储的是字符对应的数字)%c/%hhd
unsigned char1字节(ASCII码表)%c/%hhu
short2字节%hd
unsigned short2字节%hu
long4字节/8字节(64位系统)%ld
unsigned long4字节/8字节(64位系统)%lu
int4字节(c语言默认整数为int类型)%d
unsigned int4字节%u
float4字节%f/%g
double8字节(c语言默认浮点数为double类型)%lf/%lg
字面值含义字面值含义
‘\a’响铃‘\v’垂直制表
‘\b’退格‘\f’换页
‘\t’水平制表‘\r’回车(回到所在行最前面)
‘\n’换行(回到下一行最前面)‘%%’%
  • 使用sizeof关键字获取变量或者数据类型对应的内存大小,sizeof赋值语句失效

    1
    2
    3
    4
    //语法格式
    sizeof(变量名/数据类型/表达式);
    printf("%lu,%lu\n",sizeof(int),sizeof(a));//%lu-sizeof占位符
    sizeof(a=100);//a的值不会变为100

5.scanf函数

查看相关知识

scanf函数从键盘获取用户的输入,将获取的输入存储到内存的存储区(可以使用&变量名,数组名,指针表示)

1
scanf("%d,%d",&a,&b);//输入100,200 + enter

6.进制转换

查看相关知识
  • 有符号类型的二进制数最左边的位为符号位,0表示非负数,1表示负数
  • HEX:16进制,DEC:10进制,OCT:8进制,BIN:2进制
  • BYTE:1字节,WORD:2字节,DWORD:4字节,QWORD:8字节

(1)二进制转十进制

  • 非负二进制转十进制,将二进制中的每个1单独转十进制相加即可
  • 负数二进制转十进制,取反加一,转十添负

(2)十进制转二进制

  • 非负十进制转二进制,采用”除2取余,逆序排序”,先得到的余数为二进制低位,后得到的余数为二进制高位,除至商小于1
  • 负数二进制转十进制,去负转二,取反加一

(3)二进制转八进制

  • 将二进制从右至左每三位分一组,每组用0-7的数字替换
  • 八进制字面值前缀:0,例如0520
  • printf/scanf格式化占位符:%[#]o

(4)二进制转16进制

  • 将二进制从右至左每4位分一组,每组用0-9数字或者a-f字母替换
  • 16进制字面值前缀:0x,例如0xb520
  • printf/scanf格式化占位符:%[#]x

7.运算符

查看相关知识

(1)算数运算符

  • 四则运算+取余数:+-*/%

  • 赋值运算符=,一条语句可以使用多个赋值操作符,优先计算右面的操作符

  • 符合赋值操作符+=/=-=*=
  • 自增运算符++,自减运算符--,只能与存储区配合使用,将存储区的内容加一或减一,val++先使用再自增,++val先自增再使用

(2)逻辑运算符

  • 逻辑表达式的结果0-假,1-真
  • 布尔类型只包含两个整数0和1,c语言中任何数据都可以作为布尔值使用,0作为布尔值的时候,表示假,其他数据作为布尔值的时候表示真
  • 单目逻辑运算符!,可以根据一个布尔值计算出相反的布尔值,称逻辑取反
  • 双目运算符==!=>>=<<=&&||如果一个逻辑表达包含多个双目逻辑操作符必须拆成多个简单逻辑表达式再合并
  • &&||具有短路特征;短路与:若第一个表达式为假,则结果为假;短路或:若第一个表达式为真,则结果为真

(3)位运算符

  • ~按位取反,注意事项:注意数据的类型

    1
    2
    printf("~0x5a = %#x\n",~0x5a);//0xffffffa5
    printf("~0x5a = %#hhx\n",~0x5a);//0xa5
  • &按位与:任何数位与1与,结果不变;任何数位与0与,结果为0

  • |按位或:任何数位与0或,结果不变;任何数位与1或,结果为1
  • ^按位异或:任何数位与0异或,结果不变;任何数位与1异或,结果相反
  • >>右移,a>>n表示a右移n位;有符号数,右移左补符号位;无符号数,右移左补0
  • <<左移,无论是有符号数还是无符号数右移的时候,右补0
  • 在不发生高位溢出的前提下,左移1位相当于乘2,右移1位相当于除以2
  • 位操作符不会修改存储区内容

(4)三目操作符

  • 布尔值 ?表达式一 :表达式二;在?后不要使用赋值操作符

(5)优先级

  • 单目高于双目,乘除高于加减,算术高于逻辑,条件(? :)高于赋值高于逗号
  • 多数运算符具有左结合性,单目、三目和赋值运算符具有右结合性

(6)类型转换

  • 隐式类型转换,两种类型字节数不同,转换为高字节数的类型;所有的浮点运算都是以双精度进行的,即使仅含float单精度量运算的表达式,也要先转换成double型,再作运算。 char型和short型参与运算时,必须先转换成int型

    1
    printf("sizeof(1?1:0.9)=%lu\n",sizeof(1?1:0.9));//8字节
  • 隐式类型转换,若两种类型的字节数相同,且一种有符号,一种无符号,则转换成无符号类型

    1
    2
    3
    //无类型的数必须是非负数 
    printf("(-7+3u)>0=%d\n",(-7+3u)>0);//(-7+3u)>0=1 3u表示无符号3
    printf("(-7+3u)=%d\n",-7+3u);//-7+3u=-4
  • 强制类型转换,容易导致数据丢失,常用于指针

    1
    2
    int a=300;
    char c=(char)a;

8.流程控制

查看相关知识

(1)顺序结构

从上至下,顺序执行各条语句

(2)分支结构

  • if分支结构

    1
    2
    3
    4
    5
    6
    7
    if(逻辑表达式1){
    语句块1;
    }else if(逻辑表达式2){
    语句块2;
    }else{
    语句块3;
    }
  • switch分支结构

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    switch (控制表达式){
    case 常量表达式1:
    语句块1;
    break;
    case 常量表达式2:
    语句块2;
    break;
    ...
    default:
    语句块0;
    }

    控制表达式—能够表示整数;常量表达式—可以表示整数的常量;各个常量表达式不可相同

    若去掉break语句,则 自动执行下一个case所对应的代码

(3)循环结构

  • for循环

    1
    2
    3
    for (表达式1;表达式2;表达式3){
    循环体;
    }
    • 执行的顺序:1.先执行表达式1,且该语句在整个循环过程中,只执行1次;2.执行表达式2,表达式2为逻辑表达式,运算的结果为布尔值;3.表达式2为假,则结束循环;若表达式2为真,则执行循环体,再执行表达式3
    • for循环小括号内的每条语句都可以省略
    • c99规范能够在循环的小括号内临时声明循环变量,该变量只能在循环体内部使用
  • break语句

    一旦执行break语句,break语句之后的循环里的一切语句都不再执行

  • continue语句

    continue语句,跳过本次循环,执行下次循环,continue之后的语句不再执行,直接进入下一次循环

  • goto语句

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    #include<stdio.h>
    int main(void)
    {
    printf("1\n");
    goto label;//直接跳转到label位置
    printf("3\n");
    label://标签
    printf("4\n");
    return 0;
    }
  • while循环

    1
    2
    3
    while (条件表达式){
    循环体;
    }
  • do-while循环

    1
    2
    3
    do{
    循环体;
    }while(条件表达式);

    do-while循环保证循环体内部的语句至少执行1次

  • 空语句—执行语句为一个分号;如果分号没有起到结束语句的作用就是空语句

9.数组

查看相关知识

(1)一维数组

  • 数组可以用来代表多个连续的同类型存储区,这些存储区叫做数组的元素

  • 格式:存储区数据类型 数组名[存储区个数]={};

  • 数组通常不会作为整体使用,只会一个一个的使用其中的某个存储区

  • 数组里的每个存储区有一个编号,这个编号叫做数组的下标,第一个存储区的下标为0

  • 数组名称和下标一起可以用来表示数组里的存储区,格式:数组名[下标]

  • 数组名不代表存储区,它代表数组里第一个存储区的地址,也是数组的首地址

  • 获取某个存储区的首地址,格式:&存储区;a[0]表示下标为0的存储区,&a[0]表示下标为0的存储区的首地址

    1
    2
    3
    4
    5
    6
    7
    int arr[10]={0};//全0
    int arr[10]={};//全0
    int arr[10]={1}//a[0]=1,其余全是0
    int arr[]={1,2,3,4,5};//省略数组定义里的存储区个数
    int size=sizeof(a)/sizeof(a[0]);//计算数组元素的个数
    printf("数组a的地址为%p\n",a);//与a[0]地址相同
    printf("数组a的地址为%p\n",&a[0]);
  • c99规范可以使用变长数组

    1
    2
    3
    int n;
    scanf("%d",&n);
    int a[n];//不可初始化 动态数组

(2)二维数组

  • 二维数组由多个一维数组组成,二维数组的每一个元素都是一维数组,一维数组的每一个元素都是一个存储区
  • 定义二维数组需要提供两个整数,前一个整数表示一维数组的个数,后一个整数表示一维数组里的存储区个数
  • 表示二维数组的存储区,需要提供两个下标,前一个下标用来表示分组编号,后一个下标用来表示分组里的存储区编号
  • 二维数组名称后只写一个下标,这个下标作为组下标使用,这个写法可以表示组下标对应的组里第一个存储区的地址
  • 二维数组名称后加组下标也可以作为一维数组名称使用,这个一维数组里包含组下标对应组里所有的存储区

(3)数组作为形参(形参—函数小节)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
//数组作为形参 
//形参a - 数组名
//形参n - 数组元素个数
void print(int a[], int n){
for(int i = 0; i < n; i++){
printf("%d ", a[i]);
}
printf("\n");
}
int main(void){
int arr[6] = {1,2,3,4,5,6};
print(arr, 6);
return 0;
}

10.函数

查看相关知识

(1)函数使用三步骤

  • 函数声明

    若被调用函数在调用函数后面定义,则需要进行函数声明

    1
    extern 返回值类型 函数名(形参表)//可以隐藏参数名称,例如int x,可以写成int即可
  • 函数定义

    返回值类型—返回数据的类型,只能够返回一个数据;形参表—用来存储给函数的数据

    如果被调用函数没有使用return关键字指定返回值,则函数的返回值为随机数

    1
    2
    3
    返回值类型 函数名(形参表){
    函数体;
    }
  • 函数调用

    可以从调用函数向被调用函数传递多个数据,这些数据的类型可以不同

    被调用函数需要为每个传递过来的数据提供一个存储区,即形参表

    1
    函数名(实参表)

(2)递归函数

  • c语言函数可以调用自己,自己调用自己的函数叫递归函数
  • 递归函数编写步骤:1.假设递归函数已经可以使用 2.在递归调用语句前编写分支处理不可分解的情况(这种分支必须能够结束函数)

(3)补充

1
2
3
4
5
6
#include<stdlib.h>
#include<time.h>
time(0);//获取当前时间
srand(time(0));//设置一个随机数种子
rand();//生成一个随机的整数
rand() % 10;//生成0-9之间的随机数

(4)作用域和生命周期

  • 局部变量

    • 定义在函数内的变量
    • 作用域:定义的位置开始到函数结束
    • 生命周期:函数某一次执行的时间范围
    • 不能够被其他文件调用
  • 全局变量

    • 定义在函数外的变量
    • 作用域:从定义的位置开始向下的所有语句
    • 生命周期:整个程序的执行时间
    • 能够被其他文件调用
    • 全局变量未初始化,默认为0
  • 静态局部变量

    • 使用static关键字修饰
    • 作用域:定义的位置开始到函数结束
    • 生命周期:整个程序执行时间
    • 未初始化的静态局部变量,默认为0
    • 静态局部变量的初始化只在程序开始的时候执行一次
  • 静态全局变量

    • 使用static关键字修饰
    • 作用域:从定义的位置开始向下的所有语句
    • 生命周期:整个程序的执行时间
    • 不能够被其他文件调用
    • 未初始化的静态全局变量,默认为0
  • 案例

    1
    2
    3
    4
    vim命令行模式:
    vs+文件名--实现左右分屏
    ctrl+ww--切换两个文件的操作
    gcc var2.c var3.c -o var
    1
    2
    3
    4
    5
    6
    7
    8
    9
    #include<stdio.h>
    int g_a=520;//全局变量
    static int s_a=521;//静态全局变量
    static void func2(void){ //静态函数
    printf("func2:s_a=%d\n",s_a);
    }
    void func(void){
    printf("func:g_a=%d\n",g_a);
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    #include<stdio.h>
    extern int g_a;//声明其他文件的全局变量
    extern void func(void);//声明其他文件的函数
    int main(void)
    {
    printf("g_a=%d\n",g_a);
    func();
    return 0;
    }

11.指针

查看相关知识

(1)基本知识

  • 地址:字节的编号

  • 指针:可以通过地址获取存储区数据

  • 指针变量:存储地址数据的变量

  • 指针变量只能用来记录地址数据

  • 指针变量的唯一用途就是用来找另一个存储区的

  • 定义指针变量的格式

    1
    2
    3
    4
    5
    数据类型* 指针变量名;
    int *pa,*pb;
    数据类型 * 指针变量名;
    数据类型 *指针变量名;
    int* pa,pb;
  • 指针变量占据的内存大小,32位系统为4字节,64为系统为8字节

  • 定义指针变量的时候必须在变量名称前加一个*

  • 定义指针变量的时候需要提供一个类型名称,它表示这个指针是与什么类型的存储区捆绑了

  • 指针变量初始化的时候*没有参与赋值过程

  • 如果一个指针和一个存储区之间存在捆绑关系,则可以通过在指针名称前加*操作符表示它所捆绑的存储区

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    #include<stdio.h>
    int main(void)
    {
    int a=100;
    int* p=&a;
    printf("&a=%p,p=%p\n",&a,p);//变量a的地址
    printf("a=%d,*p=%d\n",a,*p);//100 100
    printf("&p=%p\n",&p);//指针p的地址
    *p=200;
    printf("a=%d,*p=%d\n",a,*p);//200 200
    return 0;
    }
  • 没有记录有效地址的指针分为两类,空指针与野指针,这两种指针都不能使用*操作符取表示它所捆绑的存储区

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    /*空指针*/
    int *p=NULL;
    printf("%d\n",*p)//error
    /*野指针,禁止出现野指针*/
    int *p;
    printf("%d\n",*p)//error
    /*安全措施*/
    if (p==NULL){
    printf("p是空指针\n");
    return -1;
    }
  • 指针支持加减整数和关系比较运算,运算结果由指针的类型决定

    1
    2
    3
    4
    int *pa=0x1000;
    p++;//p=0x1004
    char *p=0x1000;
    p++;//p=0x1001

(2)指针与一维数组

  • 如果一个指针记录了数组里第一个存储区的地址,即可通过指针找到数组的每个存储区

  • 即可指针后使用下标可以用来表示数组里的存储区

  • 数组名本身是一个指针,代表数组的首元素地址

  • arr[i]==*(arr+i),arr为数组名称

  • 大部分情况下可以使用p代替a,在计算数组长度的时候不可以使用p代替a,且数组名不可修改

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    int main(void)
    {
    int a[5]={1,2,3,4,5};
    int *p=a;
    for(int i=0;i<5;i++){
    printf("%d ",a[i]);
    }
    printf("\n");
    for(int i=0;i<5;i++){
    printf("%d ",*(a+i));
    }
    printf("\n");
    for(int i=0;i<5;i++){
    printf("%d ",p[i]);
    }
    printf("\n");
    for(int i=0;i<5;i++){
    printf("%d ",*(p+i));
    }
    int size=sizeof(p)/sizeof(p[0])//在32为系统下,指针变量占据内存大小为4字节,int类型占据4字节,因此结果为1
    a++;//error
    p++;//ok
    }

(3)指针与函数参数

  • 如果要跨函数使用存储区必须通过指针实现

  • 指针作为形参可以修改实参的值

  • 指针可以作为返回值,返回地址

  • 不可以返回局部变量的地址

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    #include<stdio.h>
    void swap(int *x,int *y){
    int tmp=*x;
    *x=*y;
    *y=tmp;
    }
    int main(void){
    int a=10,b=20;
    printf("a=%d,b=%d\n",a,b);
    swap(&a,&b);
    printf("a=%d,b=%d\n",a,b);
    return 0;
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    #include<stdio.h>
    int* read(void);
    int main(void){
    int *p=read();
    printf("*p=%d\n",*p);
    return 0;
    }
    int g_a=1024;
    static int s_a=2048;
    int* read(void){
    static int s_b=4096;
    int b=8192;
    return &g_a;//ok
    //return &s_a; //ok
    //return &s_b; //ok
    //return &b; //error 局部变量地址不能作为返回值
    }

(4)指针与const关键字

  • 功能:常量化
  • 修饰普通变量:const int a=100:不可对变量a的值进行修改
  • 修饰指针:
    • 常量指针const int *p=&a:不可以通过这种指针对它捆绑的存储区赋值,但可以对指针本身赋值
    • 指针常量 int* const p=&a:不可以对指针本身赋值,但是可以对它捆绑的存储区做赋值
    • 常量指针常量const int* const p=&a:都不可赋值

(5)泛型指针

  • 定义指针变量的时候可以使用void作为类型名称,这种指针称为无类型指针;能够与任意类型的存储区捆绑

  • 无类型指针不可使用*节引用

  • 无类型指针必须首先强制类型转换成有类型指针才能够使用*节引用

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    #include<stdio.h>
    int main(void)
    {
    int i=100;
    char c='a';
    double d=5.6;
    void *p=NULL;
    p=&i;
    printf("%d\n",*(int*)p);
    p=&c;
    printf("%c\n",*(char*)p);
    p=&d;
    printf("%lf\n",*(double*)p);
    return 0;
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    #include<stdio.h>
    int main(void){
    int a=0x12345678;
    void *p=&a;
    printf("%#x\n",*(char *)p);//0x78
    printf("%#x\n",*(char *)(p+1));//0x56
    printf("%#x\n",*(char *)(p+2));//0x34
    printf("%#x\n",*(char *)(p+3));//0x12
    printf("%#x\n",*(short *)p);//0x5678
    printf("%#x\n",*(short *)(p+2));//0x1234
    return 0;
    }

12.字符串

查看相关知识
  • 字符串由一组连续的字符构成,使用双引号引起来,这些字符的末尾必须使用’\0’字符结束

  • “abcd”==”abcd\0”

  • 在printf函数调用语句中,使用%s作为占位符,把字符串里的字符依次显示在屏幕上

  • 编译器会把字符串当作第一个字符所在存储区的地址

    1
    2
    3
    printf("%s\n","abcd");
    printf("%s\n","abcd\0");
    printf("hello,""world\n");//"hello,world"
  • 字符串表示形式

    • 字符数组

      • char str[5]:数组名表示数组首地址,不可更改;字符数组内容可以修改

        1
        2
        3
        char str[5]="abcd";
        str[0]="A";
        str="hello";//error,意思是将字符串"hello"首地址赋值给str
    • 字符指针

      • char *str=”abcd”:str存储字符串的首地址,指针变量str内容可以修改,str所捆绑的存储区内容不可修改

        1
        2
        char *str="abcd";
        *str='A';//error
  • 字符串的输入输出

    • 输出:1.printf(“%s\n”,字符串首地址);2.puts(字符串首地址)

      1
      2
      3
      4
      5
      char str[5]="abcd";
      printf("%s\n",str);//abcd
      char* str_a="abcd";
      printf("%s\n",str_a);//abcd
      printf("%c\n",*str_a);//a
      1
      2
      3
      4
      char str[100]="abcd";
      puts(str);
      char* str_a="abcd";
      puts(str_a);
    • 输入:1.scanf(“%s”,要保存字符的起始地址),遇到空格就停;2.gets(要保存字符的起始地址),读一行,但忽略最后的回车;从键盘获取一个字符串存储到内存—只能存储到字符数组中;

      由于scanf()和gets()无法知道字符串s大小,必须遇到换行符或读到文件结尾为止才接收输入,因此容易导致字符数组越界(缓冲区溢出)的情况,所以我们在定义char型数组时,应该确保其空间足够大

      1
      2
      3
      4
      char str[100]={0}
      scanf("%s",str);
      char* str_a=NULL;
      scanf("%s",str_a);//error
      1
      2
      3
      4
      char str[100]={0}
      gets(str);
      char* str_a=NULL;
      gets(str_a);//error
  • 字符串操作函数

    c语言提供一组标准函数。实现对字符串的各种操作,这些函数包含于string.h头文件

    1
    2
    3
    4
    5
    6
    7
    8
    9
    char* p="hello";
    num=strlen(p);//strlen统计字符串里有效字符的个数
    char str[20]="abc";
    strcat(str,"xyz");//合并两个字符串,第一个参数必须是字符数组,第二个字符串追加到第一个参数末尾
    strcat(str,p);//合并两个字符串,第一个参数必须是字符数组,第二个字符串追加到第一个参数末尾
    char str2[20]="abcd";
    strcpy(str2,"123456");//把第二个字符串内容拷贝到第一个字符串中,第一个参数必须为字符数组
    int ret=0;
    ret=strcmp("hello","world");//根据ASCII码,比较两个字符串的大小;返回值大于0,表示前一个字符串大
  • 字符指针数组

    • 指针数组里包含一组同类型的指针存储区

    • 字符指针数组包含多个字符指针,每个字符指针可以代表一个字符串

    • 表示方法:

      1
      2
      3
      4
      5
      6
      /*第一种*/
      char *p1="abc";
      char *p2="def";
      char* p[2]={p1,p2};
      /*第二种*/
      char* p[2]={"abc","def"};
      1
      2
      3
      4
      5
      6
      7
      8
      #include<stdio.h>
      int main(void){
      char* p[3]={"zhangsan","lisi","wangwu"};
      for(int i;i<3;i++){
      printf("%s\n",p[i]);
      }
      return 0;
      }
  • 细节

    1
    2
    char str1[] = { 'C', 'h', 'i', 'n', 'a' };
    printf("%s\n", str1);//输出乱码,由于没有加\0

13.命令行参数-main

查看相关知识
  • 主函数第一个参数是整型表示第二个参数中的指针个数,第二个参数是一个字符指针数组,这些字符串的内容来自执行程序时的命令

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    #include<stdio.h>
    #include<stdlib.h>
    int main(int argc,char* argv[]){
    for (int i=0;i<argc;i++){
    printf("argc=%d,argv[%d]=%s\n",argc,i,argv[i]);
    }
    int a=atoi(argv[1]);//atoi函数--把字符串转换成整数;atof--把字符串转换成浮点数
    int b=atoi(argv[2]);
    printf("sum=%d\n",a+b);
    return 0;
    }
    1
    2
    3
    4
    5
    6
    #运行结果
    $./main 100 200
    argc=3,argv[0]=./main
    argc=3,argv[1]=100
    argc=3,argv[2]=200
    sum=300