1.C语言入门之初识C语言

前言

今天7.21 无聊之中开始整理以前的笔记,让其看着更顺眼
现在回顾以前写的博客,总有种辣眼睛的感觉哈哈哈。

1.什么是c语言?

语言:
人和人交流:中文,英文,日语
人和计算机交流:计算机语言(c/c++/java/python)

C语言是一门通用计算机编程语言,广泛应用于底层开发。
C语言的设计目标是提供一种能以简易的方式编译、处理低级存储器、产生少量的机器码以及不需要任何运行环境支持便能运行的编程语言。

尽管C语言提供了许多低级处理的功能,但仍然保持着良好跨平台的特性,以一个标准规格写出的C语言程序可在许多电脑平台上进行编译,甚至包含一些嵌入式处理器(单片机或称MCU)以及超级电脑等作业平台。

二十世纪八十年代,为了避免各开发厂商用的C语言语法产生差异,由美国国家标准局为C语言制定了一套完整的美国国家标准语法,称为ANSI C,作为C语言最初的标准。目前2011年12月8日,国际标准化组织(ISO)和国际电工委员会(IEC)发布的C11标准是C语言的第三个官方标准,也是C语言的最新标准,该标准更好的支持了汉字函数名和汉字
标识符,一定程度上实现了汉字编程。

C语言是一门面向过程的计算机编程语言,与C++,Java等面向对象的编程语言有所不同。
其编译器主要有Clang、GCC、WIN-TC、SUBLIME、MSVC、Turbo C等。

c语言也能写软件,但更擅长底层开发

c语言标准:

二进制-->汇编语言-->B语言-->C语言
10001000 ---> ADD/SUB(助记符)
计算机科学家
各个厂商会对c语言进行定制
各个厂商定制的c语言不同,因此c语言就出现了国际标准
ANSI C
K&&R
C89/C90/C99/C11/C17
主流是C89/C90
C99都用得比较少

test.c
test.h
怎么运行起来的?
从test.c ------------------------------->> test.exe
编译器
VS不是编译器,而是集成开发环境
VS2019 集成开发环境(编辑器,编译器,连接器,调试器)
集成的是MSVC编译器
主流编译器有Clang、GCC、WIN-TC、SUBLIME、MSVC、Turbo C等

xxx.c 源文件
xxx.h 头文件
理论上可以写中文,但是不建议,VS对英文更支持

运行代码 Ctrl+F5
fn 辅助功能键

2.第一个C语言程序

#include <stdio.h>
int main()
{
   printf("hello bit\\n");
   printf("he he\\n");
   return 0;
}
//解释:
//main函数是程序的入口
//一个工程中main函数(也叫主函数)有且仅有一个

按F10 直接跳到main函数第一行开始执行

void main()
{

}
//c99之前main函数才这么写,现在已经没人这么写了
//书上这么写说明这本书比较久了
main()
{

}
//非常差的代码风格,没有返回值.没有函数类型/返回值类型

函数默认返回int类型
所谓语法,就是规定了代码的书写规则

3.数据类型

编写计算机程序的目的在于高效解决现实生活中的问题,正因如此,高级语言为我们提供了许多的数据类型。
C++ 的数据类型分为基本数据类型和自定义数据类型,其基本数据类型如下:

数据类型名长度(字节)取值范围
char1-128 ~ 127 或 0 ~ 255
bool1true, false
signed char1-128 ~ 127
unsigned char10 ~ 255
short2-32768 ~ 32767
unsigned short20 ~ 65535
int4-2147483648 到 2147483647
unsigned int40 ~ 4294967295
long8-9223372036854775808 到 9223372036854775807
unsigned long80 到 18,446,744,073,709,551,615
float43.4E-38 ~ 3.4E+38
double81.7E-308 ~ 1.7E+308
long double16长双精度型 16 个字节(128位)内存空间,可提供18-19位有效数字。

各种类型的存储大小和具体操作系统的位数有关,目前大部分系统为 64 位,与上表基本一致。

注意:C语言中没有bool类型,但可以通过stdbool头文件使用bool类型

31.45
3.145 * 10^1
//所以叫浮点数

为什么会这么多类型呢?
生活中本来就这么的类型
存在这么多的类型,其实是为了更加丰富的表达生活中的各种值

#include<stdio.h>
int main()
{
    int age = 20;// age这个变量的创建其实是要在内存中开辟空间的
    //%d 打印一个10进制的整数
    //size of计算结果是字节 byte
    printf("%d\\n", sizeof(char));  //1
    printf("%d\\n", sizeof(short)); //2
    printf("%d\\n", sizeof(int));     //4
    printf("%d\\n", sizeof(long)); //4
    printf("%d\\n", sizeof(long long)); //8
    printf("%d\\n", sizeof(float)); //4
    printf("%d\\n", sizeof(double)); //8
    printf("%d\\n", sizeof(long double)); //8
    return 0;
}

short age比int age 空间利用率更高
一个人年龄不会超过4位数

c语言只规定sizeof(long)>=sizeof(int)

c语言中0表示假,非0表示真
C99之前没有表示真假的变量
C99之后引入布尔类型

char类型中放的是字符,字符是有ASCII码值的
字符在存储的时候,存储的就是ASCII码值
所以认为char也是一种整型
字符使用单引号引起来

变量与常量

变量来源于数学,是计算机语言中能储存计算结果且在程序执行过程中可以变化的量。变量需要用名字来标识且需指定相应的数据类型,变量声明的形式如下:
数据类型 变量名1,变量名2...

注意:变量的命名不是随意的,需遵守以下规则:

  1. 变量名只能包含字母(A-Z,a-z)和数字(0-9)或者下划线(_)。
  2. 变量名首位必须是字母或下划线。
  3. 不能使用 C++ 的关键字来命名变量,以免冲突。例如:struct。
  4. 变量名区分大小写。

变量在创建时不初始化是一种不好的代码风格
一个局部变量不初始化的时候,它的值是随机
int age = 0;

变量的分类

局部变量

{} 就是一个代码块
在代码块内部定义的变量的就是局部变量

全局变量

在代码块外部定义的变量的就是全局变量

#include <stdio.h>
int global = 2019;//全局变量
int main()
{
   int local = 2018;//局部变量
   printf("global = %d\\n", global);
   return 0;
}

局部变量和全局变量名字冲突的情况下,局部变量优先,因此尽量不要相同

#include <stdio.h>
int global = 2019;//全局变量
int main()
{
   printf("global = %d\\n", global);
   int local = 2018;//局部变量
   return 0;
}
//打印的是2019 局部变量还没定义,自然先使用早就定义好的全局变量

变量使用

int main()
{
    //2个整数相加
    int num1 = 0;
    int num2 = 0;
    //scanf 是c语言提供的输入函数
    scanf("%d %d", &num1, &num2);
    //&取地址
    //scanf_s是vs编译器自己提供的,不是c标准提供的
    int sum = num1 + num2;
    printf("%d\\n", sum);
    return 0;
}

int sum在后面在一些老的编译器可能无法编译通过
c99标准有一个规定:变量要创建在当前代码块最前面
c99之后的语法支持:变量在哪使用就在哪里定义

变量作用域与生命周期

作用域

  1. 局部变量的作用域是变量所在的局部范围(大括号内)
  2. 全局变量的作用域是整个工程

生命周期

变量的生命周期指的是变量的创建到变量的销毁之间的一个时间段

  1. 局部变量的生命周期是:进入作用域生命周期开始,出作用域生命周期结束。
  2. 全局变量的生命周期是:整个程序的生命周期。
  3. 程序的生命周期与main函数生命周期相同
  4. 难以通过代码演示,生命周期描述的是一个时间段
int g = 10;
void test()
{
    printf("test::g:%d\\n", g);
}
int main()
{
    printf("g:%d\\n", g);
    test();
    int a = 10;
    {
        int b = 10;
        printf("%d\\n", b);
        printf("g:%d\\n", g);
    }
    //b的作用域结束
    //printf("%d\\n", b);
    return 0;
}

常量

常量是指具有固定值的表达式,其值在程序运行的整个过程中是不可改变的。

常量一般分为整型常量、实型常量、字符常量、字符串常量和布尔常量。

整型常量

即以文字形式出现的整数,包括正整数、负整数和零。
在 C++ 中支持十进制、八进制和十六进制的整数。
十进制表示的整型常量:由 - / + 和 若干 0 - 9 的数字组合,例如:2、- 2、356 等。
八进制表示的整型常量:以 0 开头后跟若干个 0 - 7 数字,例如:0333、06 等。
十六进制表示的整型常量:以 0x 开头后跟 0 - 9 的数字及 A-F 的字母,例如:0x12、0x3A 等。
一般的,当十进制表示的整型常量为正整数时,可省略 +;八进制和十六进制通常表示无符号的整数,所以不必添加 - / +。

实型常量

即以文字形式出现的实数,包括一般形式和指数形式。
一般形式,即正常表达的数字,例如:3.14、- 6.78 等。
指数形式,通俗来说就是我们在数学中常用的 n 乘以 10 的 x 次方,例如:3.1E+6 表示的则是 3.1 乘以 10 的 6 次方,即 3100000;6.9E-2 表示的则是 6.9 乘以 10 的 - 2 次方,即 0.069。

字符常量

用单引号括起来的一个字符称为字符常量。例如:‘a’、‘D’ 等。
例如回车、换行、制表符等既无法显示又不能输入的特殊字符,需要利用 C++ 预定义的转义序列在程序中表示。下面是常用的预定义的转义序列表:

字符常量形式含义
\n换行
\r回车
\t水平制表符
\v垂直制表符
\a响铃
\f换页
\ \字符 ’ \ ’
\ "双引号
\ ’单引号

例如:input:\n 则在输出字符串 'input:'后执行换行操作。

字符串常量

用双引号括起来的字符序列称为字符串常量。例如:“shiyanlou”、“an apple” 等。

若要在字符序列中包含双引号,例如表示语句:You choose "time" or "happy",我们则需利用预定义的转义序列 \" 来表示句中的双引号。定义其字符串常量为:"You choose \"time\" or \"happy\""

布尔常量

布尔常量只有 TRUE 和 FALSE 两种。

枚举常量

枚举:一一列举
有些值适合列举,性别
有些不适合,工资

enum Sex
{
    //枚举这种类型的可能取值-- 就是枚举常量
    MALE,
    FEMALE,
    SECRET
};
int main()
{
    printf("%d\\n", MALE); // 0
    printf("%d\\n", FEMALE); // 1
    printf("%d\\n", SECRET); // 2
    return 0;
}
// MALE的值不可以修改

常量声明

常量声明一般有两种方式,一是使用 #define 预处理、二是使用 const 关键字。

  1. 使用 #define 预处理

格式:#define name value

#define PI 3.14159
#define NEWLINE '\n'
  1. 使用 const 关键字

格式:const type name = value

const double pi = 3.14;
const char tab = '\t';

cons修饰的常变量:
具备某种常属性,无法被修改
const int num=10;
虽然不能修改,但本质上还是一个变量

int main()
{
    const int n = 100;
    //int arr[n] = { 0 };
    //报错 应输入常量表达式
    //c99标准中 支持变长数组,允许变量来指定数组大小
    //vs不支持c99标准
    return 0;
}

4.字符串&转义字符

字符与字符串

c语言没有字符串类型

''
""

这种由双引号(Double Quote)引起来的一串字符称为字符串字面值(String Literal),或者简称字符串。
字符串的末尾隐藏一个'\0'
'\0'是字符串的结束标志,不算作字符串内容

在这里插入图片描述

arr2中没有'\0'

输出结果:
abc
abc烫烫烫烫烫烫烫烫烫烫??迍鴒

'\0'与数值0是等价的

strlen求字符串长度,遇到'\0'就停止,计算结果不包含'\0'本身

#include <string.h>
int main()
{
    //可以把字符串放在字符数组中
    char arr1[] = "abc";
    char arr2[] = { 'a','b','c' };
    printf("%s\\n", arr1);
    printf("%s\\n", arr2);
    printf("%d\\n", strlen(arr1));//3
    //strlen结果不包含/0本身
    printf("%d\\n", strlen(arr2));//15(随机值)
    return 0;
}

数完abc 后面有什么不知道
15是随机值
strlen求arr2长度时,遇到'\0'才停止
'\0'只是结束标志
在这里插入图片描述

char arr2[] = { 'a','b','c','0'};

此时的'0'不是'\0'字符串结束标志
所以strlen求出来依旧是随机值
arr1里是4个字符
arr2里不止4个字符

转义字符

转义其实就是转变原来的意思。

转义字符含义
?在书写连续多个问号时使用,防止他们被解析成三字母词
\n换行
\r回车
\t水平制表符,制表符的缩进程度可以手动设置,在文本编辑器里
\v垂直制表符
\a响铃,警告字符,蜂鸣
\b退格符
\f换页
\ \字符 ’ \ ’ 用于表示一个反斜杠,防止它被解释为一个转义序列符。
\ "表示一个字符串内部的双引号
\ ’表示字符常量单引号
\dddddd表示1~3个八进制的数字。 如: \130 X
\xdddd表示2个十六进制数字。 如: \x30 0

??) 在某些编译器中就被视作为三字母词
被理解成] 右方括号
现在的编译器基本不支持三字母词了
用? 防止??)被解析为三字母词

printf("\a\a\a\a");
触发电脑蜂鸣声

\ddd(八进制)
\130
把八进制的130转换成十进制
130 ==> 0* 8^0+3 * 8^1+1 * 8^2
64+24 ==> 88
88作为ASCII值 就是X

ASCII表

字符的存储
由于只能储存二进制
给每一个字符都编码

0的ASCII值是48
a的ASCII值是97
有些字符在控制台无法打印出来

在这里插入图片描述

int main()
{
    // \xdd(dd表示2个十六进制数字)
    printf("%c",'\\x30'); // 打印结果为0
    return 0;
}
printf("%d\\n", strlen("c:\\test\\628\\test.c"));

结果是14
\t \62 \t 都是一个字符
c后面隐藏了’\0’

5.注释

  1. 代码中有不需要的代码可以直接删除,也可以注释掉
  2. 代码中有些代码比较难懂,可以加一下注释文字
  • //是c99后引入的 也是c++的注释风格
  • /**/ 是c的注释风格 缺陷:不支持嵌套注释

6.选择结构

根据判定条件的结果,选择相应执行语句的控制结构称为选择结构。

使用条件运算符实现选择结构:

利用条件运算符 ? 可以实现一个简单的选择结构,其基本形式为:

条件表达式 ? 表达式1 : 表达式2

此方式需根据条件表达式的判断结果,选择表达式 1 或者表达式 2。
其具体意义是:若条件表达式的布尔值为 TRUE ,则返回 表达式1 的值,否则返回 表达式2 的值。

使用 if 语句实现选择结构:

if 是典型的用于实现选择结构的语句,例如 if(X==1) i++; 当满足条件 X==1 时,执行语句 i++。if 语句一般分为简单的 if…else 语句、嵌套的 if 语句和 if…else if 语句三类。

简单的 if…else 语句 的基本结构为:

if(判定条件)
{
    判定条件为 TRUE 时的执行语句
}
else
{
    判定条件为 FALSE 时的执行语句
}

和使用条件运算符实现选择结构的方式类似。首先进入判定条件,若判定条件的布尔值为 TRUE 则执行 if 花括号内的语句,不执行 else 花括号内的语句;若判定条件的布尔值为 FALSE 则执行 else 花括号内的语句,不执行 if 花括号内的语句。

#include <stdio.h>
int main()
{
    int coding = 0;
    printf("你会去敲代码吗?(选择1 or 0):>");
    scanf("%d", &coding);
    if (coding == 1)
    {
        printf("坚持,你会有好offer\\n");
    }
    else
    {
        printf("放弃,回家卖红薯\\n");
    }
    return 0;
}

注意if(a=5)和if(a==5)

7.循环结构

有些事情必须日复一日的去做
终身学习
有效代码2w行! 注意是有效代码

while

在这里插入图片描述

for

在这里插入图片描述

do while

在这里插入图片描述

int main()
{
    printf("好好学习\\n");
    int line = 0;
    while (line <= 20000)
    {
        line++;
        printf("我要继续努力敲代码\\n");
        //printf("%d\\n", line);
    }
    if (line > 20000)
        printf("恭喜你找到好offer\\n");
    else
    {
        printf("火候还不够\\n");
    }
    return 0;
}

8.函数

main函数-主函数
什么是函数呢
f(x)=cx+b
f(x, y) =x+y

c语言中,把一些独立的功能封装成一个函数

int main()
{
    int num1 = 0;
    int num2 = 0;
    int sum = 0;
    printf("输入两个操作数:>");
    scanf("%d %d", &num1, &num2);
    sum = num1 + num2;
    printf("sum = %d\\n", sum);
    return 0;
}

用函数实现:

int Add(int x, int y)
{
    int z = x + y;
    return z;
}
int main()
{
    int num1 = 0;
    int num2 = 0;
    int sum = 0;
    printf("输入两个操作数:>");
    scanf("%d %d", &num1, &num2);
    sum = Add(num1, num2);
    printf("sum = %d\\n", sum);
    return 0;
}

把函数想象成一个工厂
原材料–>工厂(加工)–>产品
输入参数-函数运算-返回结果
num1,num2 ==> Add ==>sum
返回类型 函数名 函数的形式参数 函数体
如果函数定义在main后面,需要在main前声明

9.数组

一组相同类型元素的集合
int a=1;
int b=2;
int c=3;

存1~1000呢?

int main()
{
    int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };//定义一个整形数组,最多放10个元素
    //存字符 - 字符数组
    char ch1[3] = { 'a','b','c' };
    char ch2[6] = "hello";
    //hello 后面还有一个'\\0'
    //最好初始化
    int arr1[10];
    printf("%d\\n", arr1[0]);
    //vs2019 数组未初始化 不会报错
    //不完全初始化
    //int arr[10] ={ 0 };
    return 0;
}

C语言规定,数组下标从0开始

在这里插入图片描述

通过下标定位数组元素:

int main()
{
    int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
    int i = 0;
    while (i<=9)
    {
        printf("%d ", arr[i]);
        i++;
    }
    return 0;
}

10.操作符

算术操作符

+ - * / %

%操作符只能作用于整数
%取模(取余)操作符
7%2 商3余1

int main()
{
    //int n = 7.0 % 2;  //非法了
    //%只能作用于整型
    return 0;
}

7.0 / 2
/ 两端只要有小数,就是执行浮点数除法

int a = 3;
3的二进制位
00000000000000000000000000000011
32

移位操作符

>> <<

左移把二进制位向左移动,有×2的效果

位操作符

& ^ |

这里的位也是指的二进制位

&

按位与
对应二进制位同时为1才是1,只要有0就是0

int main()
{
    int a = 3;
    int b = 5;
    int c = a & b;
    //3:
    //00000000000000000000000000000011
    //00000000000000000000000000000101
    //00000000000000000000000000000001
    //同时为1才是1,只要有0就是0
    printf("%d\\n", c);
    return 0;
}

^

按位异或
对应二进制位相同为0,相异为1

int main()
{
    int a = 3;
    int b = 5;
    int c = a ^ b;
    //c:   00000000000000000000000000000110
    printf("%d\\n", c);
    return 0;
}

|

按位或
对应二进制位有1则为1,全0才是0

赋值操作符

= += -= *= /= &= ^= |= >>= <<=

+= 复合赋值符
复合赋值符写起来更方便
再懒一点直接写a++

单目操作符

!           逻辑反操作
-           负值
+           正值
&           取地址
sizeof      操作数的类型长度(以字节为单位)
~           对一个数的二进制按位取反
--          前置、后置--
++          前置、后置++
*           间接访问操作符(解引用操作符) (类型)       强制类型转换

+(加)是双目操作符 有2个操作数 a+b
单目操作符 只有一个操作数

!

逻辑反操作

int main()
{
    int c = 10;
    printf("%d\\n", !c);  //0
    return 0;
}

c语言中0表示假,非0表示真
运算符就是操作符,英文翻译结果不一样而已

sizeof

sizeof是一个操作符,不是函数!!!
用来计算变量大小
int a=10;在内存中创建了一个a变量
sizeof(a) 结果为4
计算的是变量或者类型所创建变量占据内存的大小==>单位是字节

int main()
{
    int a = 10;
    printf("%d\\n", sizeof(a));//4
    printf("%d\\n", sizeof(int));//4
    //计算的是变量或者类型所创建变量占据内存的大小--单位是字节
    printf("%d\\n", sizeof(10));//4
    //3+4 的结果是7 类型是int
    return 0;
}

~

按2进制位取反
对存在内存中的补码进行取反

int main()
{
    int a = -1;
    //-1是负整数
    //负整数的二进制有原码,反码,补码
    //内存中存储的是二进制的补码
    //10000000000000000000000000000001   -原码
    //11111111111111111111111111111110   -反码
    //11111111111111111111111111111111   -补码
    //00000000000000000000000000000000   -取反
    int b = ~a;
    printf("%d\\n", b);
    return 0;
}

++/–

++a 前置++先++后使用

//int main()
//{
//     int a = 10;
//     int b = ++a;
//     printf("%d\\n", b);
//     printf("%d\\n", a);
//     return 0;
//}
int main()
{
    int a = 10;
    int b = a++;
    //后置++ 先使用 后++
    printf("%d\\n", b);
    printf("%d\\n", a);
    return 0;
}

对于自定义类型,前置++效率更高

int main()
{
    int a = 10;
    int b = a--;
    //先使用后--
    printf("%d\\n", b);//10
    printf("%d\\n", a);//9
    return 0;
}

&

解引用操作符/间接访问操作符

*

间接访问操作符(解引用操作符)

(类型)

强制类型转换

int main()
{
    int n = 3.14;
    printf("%d\\n", n);
    return 0;
}

warning:从double转换到int 可能丢失数据
使用强制类型转换即可

int main()
{
    int n =(int) 3.14;
    printf("%d\\n", n);//3
    return 0;
}

逻辑操作符

&& 逻辑与
|| 逻辑或

int main()
{
       int a = 3;
       int b = 5;
       if ((a == 3) || (b == 3))
       {
               printf("hehe\\n");
       }
       return 0;
}

关系操作符

>
>=
<
<=
!=
==

条件操作符

可以简化if语句
exp1 ? exp2 : exp3

//int main()
//{
//	//exp1 ? exp2 : exp3
//	//简化if语句
//	int a = 0;
//	int b = 0;
//	int max = 0;
//	scanf("%d %d", &a, &b);
//	if (a > b)
//		max = a;
//	else
//		max = b;
//	printf("max is %d\\n", max);
//	return 0;
//}

int main()
{
    //exp1 ? exp2 : exp3
    //简化if语句
    int a = 0;
    int b = 0;
    int max = 0;
    scanf("%d %d", &a, &b);
    max = a > b ? a : b;
    //三目操作符
    printf("max is %d\\n", max);
    return 0;
}

逗号表达式

int main()
{
    int a = 3;
    int b = 5;
    int c = 10;
    //逗号表达式会从左向右依次计算,整个逗号表达式结果是最后一个表达式结果
    int d = (a + 2, b = a - 3, c = b + 4);
    printf("%d\\n", d);//b为0 0+4=4
    printf("%d\\n", b);//b的值发生了改变 0
    return 0;
}

下标引用/函数调用/结构成员

[] () . ->

arr[4]
[]下标引用操作符
2个操作数,一个是arr 一个是4 数组名和下标

()函数调用操作符
Add(3,5);
函数在调用时,必须有() ,操作数是函数名Add,3,5
函数调用操作符操作数不固定,可多可少

11.常见关键字

auto  break   case  char  const   continue  default  do   double else  enum   
extern float  for   goto  if   int   long  register    return   short  signed
sizeof   static struct  switch  typedef union  unsigned   void  volatile  while

关键字不能自己创建
关键字不能做变量名
define不是关键字,define是预处理指令
#define MAX 100

switch 语句中不包含continue
continue用于循环,switch不是循环

auto-自动-定义自动变量,自动创建空间自动销毁
auto int a=0;
所有的局部变量本身都是auto

注意:C++中的auto表示的自动推导类型

char
short
int
long
float
double

break-终止-用于循环中 while for do-while switch
continue-继续-用于循环,退出本次循环
case-swtich
default 默认
do
else
for
if
go to
while

const-常属性-修饰变量,修饰指针
extern-声明外部符号

return 返回,在函数中使用

signed-有符号的-修饰类型
unsigned-无符号的

void-无,空-函数返回类型,函数参数,修饰指针
volatile

c语言中可以自定义的类型:
enum-枚举
struct-结构体
union-联合体

sizeof

11.1register

register-寄存器关键字/建议性命令,不是强制的
需要cpu自己考虑是否使用

在这里插入图片描述

越往上,速度越快,空间越小,造价越高
高速缓存几十M
早期时,没有高速缓存什么的
cpu的处理速度和内存的读写速度差不多,后来cpu处理速度越来越快,但内存读写速度跟不上,就出现了高速缓存,寄存器
CPU-中央处理器
寄存器 eax ebx ecx edx esp ebp

注意! 寄存器变量不能取地址

11.2typedef

typedef-类型重定义

typedef unsigned int u_int;
int main()
{
       unsigned int num1 = 100;
       u_int num2 = 100;
       return 0;
}

EOF 文件结束标志 -end of file
#define EOF -1

11.3static

static-静态的-修饰变量和函数

修饰局部变量-称为静态局部变量修饰全局变量-称为静态全局变量修饰函数-称为静态函数

1.static修饰局部变量

//关键字static
void test()//void 表示不需要函数返回任何值
{
       int a = 1;
       a++;
       printf("%d ", a);
}
int main()
{
       int i = 0;
       while (i<10)
       {
               test();
               i++;
               //0-9都进来了,10不满足条件
               //循环10次
       }
       return 0;
}
//代码结果?  10个2

a是test里的局部变量,每次出test,生命周期到了,就会被销毁,还给操作系统
再次调用时 a依旧是1

使用static修饰a之后呢?

void test()//void 表示不需要函数返回任何值
{
    static int a = 1; //使用static修饰局部变量
    a++;
    printf("%d ", a);
}
int main()
{
    int i = 0;
    while (i < 10)
    {
        test();
        i++;
        //0-9都进来了,10不满足条件
        //循环10次
    }
    return 0;
}
//代码结果?  2 3 4 5 6 7 8 9 10 11

static修饰a后,
第二次调用test时,static int a =1;没起作用
即没有重新创建a,依旧使用了上一次的a
根据结果推测:每一次调用test函数
使用的a都是上一次函数调用留下的

内存是一块比较大的存储空间
使用内存时会划分出不同的功能区域
在学习编程语言时,我们只需要关注:
栈区,堆区,静态区
栈区:存放局部变量
堆区:进行动态内存分配,malloc free calloc realloc (函数)
静态区:全局变量放在静态区,静态变量也放在静态区
使用static修饰a后 a就跑到静态区去了

在这里插入图片描述

静态变量出了自己作用域也不销毁
相当于改变生命周期,但是a的作用域依旧还是test函数内部
static int a = 1; //使用static修饰局部变量
通过F11调试时发现,都没读取这句话,
进一步查看反汇编时,这句话都没进行汇编
在这里插入图片描述

       static int a = 1; //使用static修饰局部变量
       a++;
00841851  mov         eax,dword ptr [a (084A000h)]

00841856  add         eax,1

00841859  mov         dword ptr [a (084A000h)],eax

调试的时候已经是exe文件了
全局变量/静态变量的创建是在编译期间进行的。

2.static修饰全局变量

//如果想使用来自其他文件(外部文件)的全局变量,先要声明一下
extern int g_val;
//extern是一个关键字,专门用来声明外部符号的
//g_val在add.c文件创建的
int main()
{
    printf("%d\\n", g_val);
    return 0;
}

如果static修饰全局变量呢?
static int g_val = 2021;

可以编译,但无法链接

本来全局变量在整个工程都可以使用
但使用static修饰后,似乎作用域变小了??

一个全局变量在整个工程的其他文件内部能被使用,是因为全局变量具有外部链接属性
当一个全局变量被static修饰时,这个变量的外部链接属性就变成了内部链接属性,
使得这个全局变量只能在自己所在的源文件内部使用,其他文件不能再使用
全局变量的外部链接属性变成内部链接属性,从而使他的作用域变小了,
生命周期还是没有发生变化
静态变量和全局变量生命周期都一样,还是程序结束时生命周期结束,存储位置也没有变化,依旧在静态区

在这里插入图片描述

3.static修饰函数

//Add是外部函数,得先声明
extern int Add(int x,int y);
int main()
{
    int a = 10;
    int b = 20;
    int ret = Add(a, b);
    printf("%d\\n", ret);
    return 0;
}

说明函数也是具有外部链接属性的

如果给函数加上static后,也是把函数的外部链接属性变为内部连接属性,不能在test.c内使用add.c的函数了,函数只能在自己所在的源文件内部使用

总结:

static修饰局部变量时,改变了变量的存储类型(栈区存储->静态区存储)
从而使得静态的局部变量出了自己的作用域也不会销毁,相当于改变了局部变量的生命周期 a的作用域依旧是test{}里

一个函数/全局变量被static修饰,使得这个函数/全局变量只能在本源文件内使用,不能在其他源文件内使用。用法:加上static后,函数/全局变量就可以不让其他人使用,只能自己用

12.#define定义常量和宏

//define定义标识符常量
#define M 100
#define STR "hehe"
int main()
{
    printf("%d\\n", M);
    printf("%s\\n", STR);
    return 0;
}

宏和函数非常相似
X 和 Y是有个参数,需要括起来
X + Y的结果也需要括起来
无返回类型,无参数类型
一般约定俗称用大写,非要用小写也行

//#define 定义宏
#define ADD(X,Y) ((X)+(Y))
int main()
{
    int a = 10;
    int b = 20;
    int ret = ADD(a, b);
    printf("%d\\n", ret);
    return 0;
}

13.指针

内存

谈到指针就必须谈内存
4G,8G
内存的管理类比==>国土面积的划分

在这里插入图片描述

利用F10调试并查看a在内存中的地址:
按下F10 ==> 调试 ==> 窗口 ==> 内存 ==> 内存1

在地址中输入&a即可查看a的地址

一个小格子就是一个内存单元,1byte->1字节
给内存单元进行编号,把内存单元的编号 ==> 地址
地址也叫指针,把地址存进变量p
p就是指针变量

指针变量

#include<stdio.h>
int main()
{
	int a = 10;
	//4byte
	//& 取地址操作符
	//a占4个字节,每个字节都有编号
	//取出的是所占4个字节空间的第一个字节的地址(地址小的那个字节)
	printf("%p\\n", &a);
	//%p 地址的打印格式
	int* p = &a;
	*p = 20;
	//* 解引用操作符,*p通过p中的值,找到p所指的对象
	//*表示p是指针变量
	//int 表示p指向的对象的类型是int类型
	//p是用来存地址的,所以把p称为指针变量
	printf("%p\\n", p);
	printf("%d\\n", a);
	return 0;
}

*告诉我们p是指针变量
int表示p是指向一个整型变量的
存好了变量的地址是为了有一天通过指针来改变该变量的值
最底层的原理:就是为了通过地址改变值
&a 获取a的地址
我们把地址也称为指针
地址放在指针变量中
在锤子的眼里,什么都是钉子
在指针变量眼里,什么都是地址

注意:
每次打印a的地址时,每次结果都不一样,
因为每次编译时会给变量重新分配内存空间

指针变量的大小?

#include <stdio.h>
//指针变量的大小取决于地址的大小
//32位平台下地址是32个bit位(即4个字节)
//64位平台下地址是64个bit位(即8个字节)
int main()
{
   printf("%d\\n", sizeof(char *));//4
   printf("%d\\n", sizeof(short *));//4
   printf("%d\\n", sizeof(int *));//4
   printf("%d\\n", sizeof(double *));//4
   return 0;
}

要想知道指针变量有多大----地址的存放需要多大空间?----地址是如何产生的?地址是什么样的数据?
32位机器—32根地址线/数据线(物理的电线–通电–>高电平/低电平 即1/0)
32个地址线都是低电平的话
00000000000000000000000000000000
00000000000000000000000000000001
00000000000000000000000000000010

10000000000000000000000000000000

1111111111111111111111111111111111111
总共能产生2^32个编号
32个bit就可以存32个0/1组成的二进制序列
也就是4byte
所以指针变量大小也就是4个字节
同理64位机器,需要64bit也就是8byte存地址

结论:

指针大小在32位平台是4个字节,64位平台是8个字节。

x86 代表32位机器
x64 表示64位
1个二进制序列管理1个byte空间

则2^32 二进制序列管理 2^32 byte空间

4,294,967,296 byte-----4GB

总结:

1个内存单元的大小是1个字节
管理这个内存单元需要的地址的大小是4个字节
32位二进制序列组成一个地址,这个地址管理1个字节,是一个字节的编号,这个地址本身大小是4个字节

14.结构体

人:复杂对象 (名字,年龄,性别,…)
书:复杂对象(书名,作者,出版社,定价,书号,…)
‘a’ —一个字节
“中国”
1个汉字占2个字节空间
printf("%d\n", strlen("中国")); //结果为4,strlen计算时不包括末尾的'\0'

结构体成员访问操作符

. ->
结构体指针->结构体成员
结构体变量.结构体成员

#include<string.h>
struct Stu
{
    //结构体成员
    char name[20];//名字
    int age;//年龄
    char sex[10]; //性别
    //char id[15]; //学号
    //[]空间大小必须明确指定
    //....还可以继续添加
};
int main()
{
    struct Stu zhangsan = { "张三",30,"男" };
    struct Stu lisi = { "李四",24,"保密" };
    printf("%s %d %s\\n", zhangsan.name, zhangsan.age, zhangsan.sex);
    printf("%s %d %s\\n", lisi.name, lisi.age, lisi.sex);
    //.为结构成员访问操作符
    //
    //->操作符
    //printf("%d\\n", strlen("中国"));
    struct Stu* pl=&lisi;
    printf("%s %d %s\\n", (*pl).name, (*pl).age, (*pl).sex);
    printf("%s %d %s\\n", pl->name, pl->age, pl->sex);
}

尾声

🌹🌹🌹

写文不易,如果有帮助烦请点个赞~ 👍👍👍

Thanks♪(・ω・)ノ🌹🌹🌹

😘😘😘

👀👀由于笔者水平有限,在今后的博文中难免会出现错误之处,本人非常希望您如果发现错误,恳请留言批评斧正,希望和大家一起学习,一起进步ヽ( ̄ω ̄( ̄ω ̄〃)ゝ,期待您的留言评论。
附GitHub仓库链接

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值