入门C语言基础

发布于:2024-10-11 ⋅ 阅读:(71) ⋅ 点赞:(0)

入门C语言基础

1 基础语法

1.1 整形

类型 存储大小 值范围
char 1 字节 -128 到 127 或 0 到 255
unsigned char 1 字节 0 到 255
signed char 1 字节 -128 到 127
int 2 或 4 字节 -32,768 到 32,767 或 -2,147,483,648 到 2,147,483,647
unsigned int 2 或 4 字节 0 到 65,535 或 0 到 4,294,967,295
short 2 字节 -32,768 到 32,767
unsigned short 2 字节 0 到 65,535
long 4 字节 -2,147,483,648 到 2,147,483,647
unsigned long 4 字节 0 到 4,294,967,295
#include <stdio.h>

int main() {
    // 1 整形
    char i =127;
    printf("%d\n",i);
    unsigned char i1=255;
    printf("%d\n",i1);
    short i2=233;
    printf("%d\n",i2);
    long i3=123456;
    printf("%d\n",i3);

    int i4=1234567;
    printf("%d\n",i4);
    // 查看int类型再我们机器上占几个字节
    printf("%d\n",sizeof(i4));
    printf("%d\n",sizeof(int));

    return 0;
}

1.2 浮点型

类型 存储大小 值范围 精度
float 4 字节 1.2E-38 到 3.4E+38 6 位有效位
double 8 字节 2.3E-308 到 1.7E+308 15 位有效位
long double 16 字节 3.4E-4932 到 1.1E+4932 19 位有效位
#include <stdio.h>

int main() {
    // 1 浮点型
    printf("%d\n",sizeof(float));
    printf("%d\n",sizeof(double));
    printf("%d\n",sizeof(long double));
    // %f代表一般计数法输出
    float f1=1.234567F;
    double f2=1.2345678;
    printf("%f\n",f1);
    printf("%f\n",f2);


    return 0;
}

1.3 常量

#include <stdio.h>

int main() {
    // 1 常量
    const int MAX_VALUE=99;
    //MAX_VALUE=199;
    printf("%d\n",MAX_VALUE);

    return 0;
}

1.4 运算符

1.4.1 算数运算符

A 的值为 10,变量 B 的值为 20

运算符 描述 实例
+ 把两个操作数相加 A + B 将得到 30
- 从第一个操作数中减去第二个操作数 A - B 将得到 -10
* 把两个操作数相乘 A * B 将得到 200
/ 分子除以分母 B / A 将得到 2
% 取模运算符,整除后的余数 B % A 将得到 0
++ 自增运算符,整数值增加 1 A++ 将得到 11
自减运算符,整数值减少 1 A-- 将得到 9
#include <stdio.h>

int main() {
    // 1 算数运算符
    int a=10;
    int b=20;
    int c;
    c=a+b;
    printf("a+b的结果是:%d",c);
    c = a - b;
    printf("a-b结果是 %d\n", c);
    c = a * b;
    printf("a * b 结果是%d\n", c);
    c = b / a;
    printf("b / a的值是 %d\n", c);
    c = 10 % 3;
    printf("10 % 3取整除的值是 %d\n", c);
    c = a++;  // 赋值后再加 1 ,c 为 10,a 为 11
    c=++a;
    printf("赋值后再加 的值是 %d,a的值为:%d\n", c, a);
    c = a--;  // 赋值后再减 1 ,c 为 11 ,a 为 10
    printf("赋值后再减 1的值是 %d,a的值为:%d\n", c, a);

    return 0;
}

1.4.2 关系运算

运算符 描述
== 检查两个操作数的值是否相等,如果相等则条件为真。
!= 检查两个操作数的值是否相等,如果不相等则条件为真。
> 检查左操作数的值是否大于右操作数的值,如果是则条件为真。
< 检查左操作数的值是否小于右操作数的值,如果是则条件为真。
>= 检查左操作数的值是否大于或等于右操作数的值,如果是则条件为真。
<= 检查左操作数的值是否小于或等于右操作数的值,如果是则条件为真。

1.4.3 逻辑运算

运算符 描述
&& 称为逻辑与运算符。如果两个操作数都非零,则条件为真。
|| 称为逻辑或运算符。如果两个操作数中有任意一个非零,则条件为真。
! 称为逻辑非运算符。用来逆转操作数的逻辑状态。如果条件为真则逻辑非运算符将使其为假。
#include <stdio.h>
int main() {
    int a = 10;
    int b = 20;
    if (a && b) {
        printf("条件为真\n");
    }

    return 0;
}

1.4.4 赋值运算符

运算符 描述
= 简单的赋值运算符,把右边操作数的值赋给左边操作数
+= 加且赋值运算符,把右边操作数加上左边操作数的结果赋值给左边操作数
-= 减且赋值运算符,把左边操作数减去右边操作数的结果赋值给左边操作数
*= 乘且赋值运算符,把右边操作数乘以左边操作数的结果赋值给左边操作数
/= 除且赋值运算符,把左边操作数除以右边操作数的结果赋值给左边操作数
%= 求模且赋值运算符,求两个操作数的模赋值给左边操作数
<<= 左移且赋值运算符
>>= 右移且赋值运算符
&= 按位与且赋值运算符
#include <stdio.h>
int main() {
    int a = 21;
    int c=10;
    c += a;
    printf("%d\n", c);
    c *= a;
    printf("%d\n", c);

    return 0;
}

1.4.5 其他

运算符 描述 实例
sizeof() 返回变量的大小。 sizeof(a) 将返回 4,其中 a 是整数。
& 返回变量的地址。 &a; 将给出变量的实际地址。
* 指向一个变量。 *a; 将指向一个变量。
? : 条件表达式 如果条件为真 ? 则值为 X : 否则值为 Y
#include <stdio.h>
int main() {
    int a = 10;
    int b = (a == 1) ? 20 : 30;
    printf("b 的值是 %d\n", b);
    return 0;
}

printf 占位符

占位符 描述
%d%i 有符号十进制整数(int)
%u 无符号十进制整数(unsigned int)
%o 无符号八进制整数
%x%X 无符号十六进制整数(%x 使用小写字母,%X 使用大写字母)
%c 字符(实际上是一个整数,按 ASCII 值输出)。
%f 浮点数(double)
%e%E 浮点数,指数表示法(%e 使用小写字母 e%E 使用大写字母 E
%g%G 浮点数,根据数值的大小自动选择 %f%e%g 使用小写字母,%G 使用大写字母)。
%a%A 浮点数,十六进制表示法(%a 使用小写字母,%A 使用大写字母)
%p 指针地址(通常用于 void* 类型
%s 字符串(char 数组或指针)
%n 不输出任何内容,但会将已写入的字符数存储在提供的 int* 变量中。

1.5 if判断

#include <stdio.h>
int main() {
    int num;
    printf("请输入一个数字:");
    scanf("%d",&num);

    if(num>90){
        printf("优秀");
    } else if (num>=60 && num<=90){
        printf("及格");
    } else{
        printf("不及格");
    }
    return 0;
}

1.6 循环

循环类型 描述
while 循环 当给定条件为真时,重复语句或语句组。它会在执行循环主体之前测试条件。
for 循环 多次执行一个语句序列,简化管理循环变量的代码。
do…while 循环 除了它是在循环主体结尾测试条件外,其他与 while 语句类似。
#include <stdio.h>

int main() {
//    int num=0;
    //while循环
//    while (num<10){
//        printf("%d\n",num);
//        num++;
//    }

    //do while 循环
//    do{
//        printf("%d\n",num);
//        num++;
//    } while (num<10);

// for 循环
//    for (int i = 0; i < 10; ++i) {
//        printf("%d\n",i);
//    }

    // 死循环
    for (;;) {
        printf("死循环");
    }

    return 0;
}

1.7 函数

#include <stdio.h>

# 返回值类型  函数名 (参数a , 参数b)
int add(int a, int b){
    return a+b;
}

int main() {
    int res=add(3,4);
    printf("%d\n",res);
    return 0;
}

1.8 字符串和字符串

# c中没有字符串
# 用字符数组表示字符串
#include <stdio.h>

int main() {
    // 定义字符
    char a='B';
    printf("%c\n",a);

    //定义字符串
    char s[]="justin";
    char s1[]={'j','u','s','t','i','n','\0'};
    printf("%s\n",s);
    printf("%s\n",s1);
    return 0;
}

1.9 数组

# 对于数组来说,内部元素是挨个存放,内存地址相邻。

# 它可以存储一个固定大小的相同类型元素的顺序集合。数组是用来存储一系列数据,是一系列相同类型的变量
#include <stdio.h>

int main() {
    // 定义char类型数组--》使用字符数组表示字符串,但是如果是数组的表示形式,必须加 \0,表示字符串结尾, 支付字符数组需要\0 结尾
    char s1[]={'j','s','s','s','s','s','\0'};
    // 定义int类型数组
    int i1[]={22,33,44,55};

    // 数组取值赋值
    printf("%d\n",i1[1]);
    printf("%c\n",s1[1]);

    // 数组大小
    printf("%d\n", sizeof(i1)); // 64为机器--》一个int占4个bytes---》数组大小为4--》占16个字节
    printf("%d\n", sizeof(i1)/sizeof(i1[0])); // 数组大小为4

    char c[]={22,33,44,55};
    printf("%d\n", sizeof(c));  // 4个字节
    printf("%d\n", sizeof(c[0])); // 一个char类型占1个字节
    printf("%d\n", sizeof(c)/sizeof(c[0]));


    return 0;
}

2 指针

# 1 指针是存储 变量内存地址的变量
	-指针是变量:intfloat一样,是变量
    -指针是存放 内存地址 0x22123
    
# 2 下图解释
int v1=666;
int *v2=&v1;

在这里插入图片描述

2.1 定义指针类型变量和取变量地址


int main() {
    // 定义指针类型变量,取变量地址
    int v1=666;
    int *v2=&v1;
    printf("v2的值是:%p\n",v2);  // 000000000061FE14

    return 0;
}

在这里插入图片描述

2.2 指针类型变量解引用

int main() {
    // 定义指针类型变量,取变量地址
    int v1=666;
    int *v2=&v1;
    printf("v2的值是:%p\n",v2);  // 000000000061FE14
//    int a=*v2;
    printf("v2的指向具体的值是:%d\n",*v2);
    printf("v1值是:%d\n",v1);

    return 0;
}

2.3 修改变量的值- 通过指针修改变量值

#include <stdio.h>

/* 三句重点的话
 1   * 放在变量定义前 [  int *v2  ],表示执行 这个类型的指针
 2   & 放在变量前    [  &v1      ],表示取这个变量的地址---》赋值给一个指针类型
 3   * 放在指针类型变量前,   [*v2]   表示解引用--》把地址反解成真正的值
 */

// python中所有类型的对象,都是c语言结构体的指针

int main() {
    int v1=666;
    int *v2=&v1;
    //1 通过值改变量
    v1=888;
    printf("v1的值是:%d\n",v1);
    printf("v2指向具体的值是:%d",*v2);  // 888

    //2 通过指针修改变量值
    *v2=999;
    printf("v1的值是:%d\n",v1);



    return 0;
}

在这里插入图片描述

2.4 指针零值和长度

int main() {
    int *v2=NULL;  // 如果不赋初值,是错乱的,要么赋值,要么赋为 NULL,表示空指针
    // printf("%p\n",v2);
    if(!v2){
        printf("指针是空的\n");
    }
    printf("%d\n",sizeof(v2)); // 无论什么类型的指针,都是8,指针这个变量长度是固定的

    return 0;
}

下图就是python 变量能够随便更改数据类型的实现。实际上,python中的变量指向的都是同一类型的内存地址,再由内存地址执行不同类型的值
在这里插入图片描述

2.6 指针类型参数

#include <stdio.h>


void changeA(int a){
    a=999;
    printf("内部的a是:%d\n",a);// 999
}

void changPointA(int *p){
    *p=666;
    printf("内部的p指向的值是:%d\n",*p);// 666
}
int main() {
    int a =1;
    changeA(a);
    //printf("外部的a是:%d\n",a);  // 1 c语言函数传参,是复制一份传过去
    changPointA(&a);  // 复制一份传入---》把地址复制一份传入
    printf("外部的a是:%d\n",a); // 666
    return 0;
}

2.7 指针运算(数组的指针)

# 1 数组的指针                             指针数组
 int *p=&[1,2,3,]                     [*v1,*v2,*v3]
    

在这里插入图片描述

#include <stdio.h>


int main() {
    // 定义一个数组
    int a[3]={11,22,33};
    int *p=&a;
    //int *p=&a[0];
    printf("p的值是:%p\n",p); // 内存地址:000000000061FE0C  数组第0个位置的
    printf("p解引用是:%d\n",*p); // 是数组的第0个位置,11

    // 指针运算---》取数组第二个位置的值
    printf("数组第二个位置值是:%d\n",a[1]);
    // 使用指针运算得到  通过指针的 ++  就可以依次拿到数组的下一个位置
//    printf("使用指针运算得到数组第二个位置值:%d\n",*p++);
//    printf("使用指针运算得到数组第二个位置值:%d\n",*p);
//    printf("使用指针运算得到数组第二个位置值:%d\n",*(++p));
//    printf("使用指针运算得到数组第二个位置值:%d\n",*(++p));
//    printf("使用指针运算得到数组第三个位置值:%d\n",*(p+2));

    return 0;
}

在C语言中,无论是通过指针还是通过数组下标来访问数组元素,如果访问超出了数组定义的边界(例如,对于定义为a[3]的数组,尝试访问a[4]),这种行为是未定义的(undefined behavior)。尽管在某些情况下,程序可能会在内存中“拿到”一个值(这通常是因为内存布局的原因,使得越界访问没有立即导致程序崩溃),但这并不意味着这个值是有效的或安全的。这种越界访问实际上会导致内存溢出(或称为内存越界),它可能引发各种不可预测的问题,包括数据损坏、程序崩溃或安全漏洞。

2.8 指针的指针

int main() {
    int a = 100;
    int *p1 = &a;
    int **p2 = &p1;
    int ***p3 = &p2;
    printf("p3的值是:%p\n", p3); //000000000061FE00
    printf("p3指向的值是(其实是p2的值):%p\n", *p3); //000000000061FE08
    printf("p2的值是:%p\n", p2); //000000000061FE08

    printf("通过p3拿到真正的a的值:%d\n", ***p3);

    return 0;
}

2.9 字符串案例

2.9.1 字符串格式

#include <stdio.h>


int main() {

    // 1  定义一个char数组
    char a[6];
    char *p =&a;
    //等同于下面
    //    a[0]='j';
    //    a[1]='u';
    //    a[2]='s';
    //    a[3]='t';
    //    a[4]='i';
    //    a[5]='n';
    //字符串格式
    sprintf(p,"%c",'j');
    p+=1;
    sprintf(p,"%c",'u');
    p+=1;
    sprintf(p,"%c",'s');
    p+=1;
    sprintf(p,"%c",'t');
    p+=1;
    sprintf(p,"%c",'i');
    p+=1;
    sprintf(p,"%c",'n');

    printf("a的值:%s\n",a);

    return 0;
}


2.9.2 判断字符串包含关系

#include <stdio.h>
#include <string.h>

int main() {
    char name[]="justin is handsome";
    //判断handsome是否在字符串中
    char *res=strstr(name,"handsome");
    if(res){
        printf("存在");
    } else{
        printf("不存在");
    }


    return 0;
}



2.9.3 字符串相加-复制字符串

#include <stdio.h>
#include <string.h>
#include <stdlib.h>

int main() {
    char name[]="justin";
    char role[]="teacher";
    // 把上述俩字符串放到一个字符串中

    // 分配内存空间
    char *s=malloc(strlen(name)+ strlen(role)+1);
    strcpy(s,name);//字符串复制,把name内容复制到s指针上
    strcat(s,role); // 字符串拼接,把s指针后面追加role字符串
    printf("%s\n",s);


    return 0;
}



3 结构体

# 一种用户自定义的数据类型,它允许存储不同类型的数据项
	相当于类,放了一堆属性

# 结构体中的数据成员可以是基本数据类型(如 int、float、char 等),也可以是其他结构体类型、指针类型等

3.1 结构体基本使用

#include <stdio.h>

// 1定义结构体 Person
struct Person{
    char name[30];
    int age;
};

int main() {
    // 1 使用结构体
    struct Person p1={"justin",19};
    printf("人名是:%s,年龄是:%d\n",p1.name,p1.age);


    //2 结构体指针
    struct Person *p2=&p1;

//    printf("人名是:%s,年龄是:%d\n",(*p2).name,(*p2).age);  // 这个一般不用
    printf("人名是:%s,年龄是:%d\n",p2->name,p2->age);  // 用这个
    return 0;
}



3.2 单向链表

在这里插入图片描述

#include <stdio.h>


struct Node{
    int data;
    struct Node *next;
};


int main() {
    struct Node v3={33};
    struct Node v2={22,&v3};
    struct Node v1={11,&v2};

    printf("v1的数据是:%d\n",v1.data);

    printf("v1的下一个节点的数据(v2的data):%d\n",v1.next->data);

    printf("v1的下一个节点的下一个节点数据(v2的data):%d\n",v1.next->next->data);
    return 0;
}

3.2 双向链表

在这里插入图片描述

#include <stdio.h>


struct Node{
    int data;
    struct Node * prev;
    struct Node *next;
};


int main() {
    struct Node v3={33};
    struct Node v2={22};
    struct Node v1={11};

    v1.next=&v2;

    v2.next=&v3;
    v2.prev=&v1;

    v3.prev=&v2;


    printf("v1的值是:%d\n",v1.data);
    printf("通过v1拿到v2的值是:%d\n",v1.next->data);
    printf("通过v1拿到v3的值是:%d\n",v1.next->next->data);


    printf("v3的值是:%d\n",v3.data);
    printf("通过v3拿到v2的值是:%d\n",v1.prev->data);
    printf("通过v3拿到v1的值是:%d\n",v1.prev->prev->data);

    return 0;
}

3.3 双向循环链表

在这里插入图片描述

#include <stdio.h>


struct Node{
    int data;
    struct Node * prev;
    struct Node *next;
};


int main() {
    struct Node v3={33};
    struct Node v2={22};
    struct Node v1={11};

    v1.next=&v2;
	v1.prev = &v3

    v2.next=&v3;
    v2.prev=&v1;

    v3.prev=&v2;
    v3.next=&v1;


    printf("v1的值是:%d\n",v1.data);
    printf("通过v1拿到v2的值是:%d\n",v1.next->data);
    printf("通过v1拿到v3的值是:%d\n",v1.next->next->next->next->data);


    printf("v3的值是:%d\n",v3.data);
    printf("通过v3拿到v2的值是:%d\n",v1.prev->data);
    printf("通过v3拿到v1的值是:%d\n",v1.prev->prev->data);

    return 0;
}

4 预处理和头文件

4.1 预处理

#  预处理,在程序编译之前会先运行的。
#include <stdio.h>

# define ME 200
# define SIZE 18

int main() {
//    SIZE=99;// 程序运行过程中改就不行了
    printf("%d\n",ME); // 只能用
    return 0;
}

4.2 头文件

项目目录
├── main.c   主文件
├── utils.c  些函数方法
└── utils.h  用来注册能被导出的函数

4.2.1 main.c

#include <stdio.h>
#include "utils.h"  // 内置的用 <> ,自定义的用 ""
int main() {

    int res=add(4,5);
    printf("%d\n",res);
}


4.2.2 utils.c


int add(int a, int b) {
    return a + b + 100;
}

int a(){
    return 11;
}

4.2.3 utils.h


int add(int a, int b);

4.3 Python源码中头文件的使用


#1 cpython解释器是用c写的--》里面都是c代码

https://www.python.org/downloads/source/

# 2 python中所有数据类型,都是c语言结构体指针
# github上cpython源码:https://github.com/python/cpython

#Objects 下会有咱们得列表,字典等结构体
https://github.com/python/cpython/tree/main/Objects# 

# 列表:的源代码
https://github.com/python/cpython/blob/main/Objects/listobject.c
    
# 列表的头文件
https://github.com/python/cpython/blob/main/Include/listobject.h

网站公告

今日签到

点亮在社区的每一天
去签到