零基础C入门到深入简出

零基础C入门到深入简出

第一章 C语言概述

为什么学习C语言?
C语言是一门基础的编程语言,适用底层开发、硬件开发、跨平台等场景,场景很多不管你从事什么软件或硬件开发很容易遇到C语言,很有必要学习。

从事开发人员还是零基础,不要语言束缚思维,真正用场景思维逻辑、算法等才是主要,语言可以快速学习,项目在做时候部分不理解可以查找资料不齐。

#include <stdio.h>//包含有关标准库的信息
int main() {//主方法
  //main函数调用库函数printf可打印字符序列,\n代表换行符
    printf("Hello, World!\n");
    return 0;
}

1.1、数据类型、运算、表达式

1、“类型”是对数据的抽象
2、类型相同的数据有相同的表示形式、存储格式以及相关的操作
3、 程序中使用的所有数据都必定属于某一种数据类型

请添加图片描述
在这里插入图片描述

变量说明:
1、程序通过变量来申请和命名内存空间int k=3;
2、通过变量名访问内存空间
3、(一段连续)内存空间的别名(是一个门牌号)
在这里插入图片描述

printf中的转换说明占位符:
1、%d 打印十进制整数
2、%f 打印浮点数
3、%c打印字符
4、%s 打印字符串
5、%ld打印长整型

在这里插入图片描述

运算符:
(1) 算术运算符(+, -, *, /, %, ++, - -)

(2) 赋值运算符(=)

(3) 圆括号运算符(( ))

(4) 强制类型转换运算符(( 类型))

(5) 求字节数运算符(sizeof)

(6) 关系运算符(<, >, <=, >=, ==, !=) 。

(7) 逻辑运算符(!, &&, ||) 。

在这里插入图片描述

1.2顺序设计

程序从main函数的起始点开始执行,在main函数的结束点结束执行。程序按照代码的先后顺序逐行执行,每个语句依次执行完毕后再执行下一条语句。

顺序程序设计涉及以下几个方面:

1、变量声明和定义:在程序开始前,可以声明和定义需要使用的变量,为其分配内存空间。

2、输入输出:程序可以通过输入函数(如scanf)接收用户的输入,并通过输出函数(如printf)显示结果。

3、运算符和表达式:C语言提供了丰富的运算符和表达式,用于对变量进行数学和逻辑运算,从而得到期望的结果。

4、函数调用:可以调用已经定义好的函数来完成特定的功能。函数可以有返回值和参数,通过函数调用实现代码的模块化和重用。

5、控制流语句:C语言提供了控制流语句,如条件语句(if语句)、循环语句(for、while、do-while循环),用于根据条件或者需求来选择执行不同的代码块。

6、数组和指针:C语言支持数组和指针的使用,可以有效地处理大量数据和引用内存地址。

7、结构体和枚举:结构体和枚举类型可以帮助组织和管理复杂的数据结构和数据类型。

第二章 控制语句与数组

在这里插入图片描述

2.1分支结构程序

1、if语句:用于根据条件执行不同的代码块

if (condition) {
    // 执行语句块1
}
else {
    // 执行语句块2
}

2、switch语句:根据表达式的值选择执行不同的代码块

switch (expression) {
    case value1:
        // 执行语句块1
        break;
    case value2:
        // 执行语句块2
        break;
    default:
        // 执行默认语句块
        break;
}

3、循环语句中的break和continue:用于控制循环的执行流程(略)

break语句:跳出当前循环或switch语句。
continue语句:跳过当前循环的剩余代码,进入下一次循环迭代。

4、goto语句:无条件地将控制转移到指定的标签处

goto label;
// ...
label: 
    // 执行语句块

5、?: 三元运算符:根据条件选择不同的值

result = (condition) ? value1 : value2;

2.2循环结构程序

1、for循环

for (初始化表达式; 循环条件; 更新表达式) {
    // 循环体
}

初始化表达式:在循环开始前执行一次,用于初始化循环变量。
循环条件:每次循环开始前判断是否满足条件,如果满足则执行循环体,否则跳出循环。
更新表达式:在每次循环结束后执行一次,用于更新循环变量的值。
2、while循环,先判断条件

while (循环条件) {
    // 循环体
}

3、do-while循环,先执行一次循环体

do {
    // 循环体
} while (循环条件);

2.3、数组

2.3.1、一维数组

数组语法type arrayName[arraySize];
int mark[100];本质是什么?

在这里插入图片描述

1、数组初始化

int a[10] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };//定义一个数组,同时初始化所有成员变量
int a[10] = { 1, 2, 3 };//初始化前三个成员,后面所有元素都设置为0
int a[10] = { 0 };//所有的成员都设置为0
//[]中不定义元素个数,定义时必须初始化
int a[] = { 1, 2, 3, 4, 5 };//定义了一个数组,有5个成员
char name[10] = "John"; // 其他元素默认初始化为'\0'
2.3.2、二维数组

type arrayName[rowSize][columnSize];

初始化数组

int a[3][4] = {0,1,2,3,4,5,6,7,8,9,10,11};

int a[3][4] = { {0, 1, 2, 3} , /*  初始化索引号为 0 的行 */
              {4, 5, 6, 7} ,   /*  初始化索引号为 1 的行 */
              {8, 9, 10, 11}   /*  初始化索引号为 2 的行 */
};

//案例一
int a[3][4] = {{1},{2},{3}};
等价:
int a[3][4] = {{1,0,0,0},{2,0,0,0},{3,0,0,0}};
//案例二
int a[3][4] = {{1}};
等价:
int a[3][4] = {{1,0,0,0},{0,0,0,0},{0,0,0,0}};
char names[2][10] = {"John", "Alice"}; // 其他元素默认初始化为'\0'

遍历:

int matrix[3][3] = {{1, 2, 3}, {4, 5, 6}, {7, 8, 9}};

// 使用嵌套循环遍历二维数组
for (int i = 0; i < 3; i++) {
    for (int j = 0; j < 3; j++) {
        printf("%d ", matrix[i][j]);
    }
    printf("\n");
}

int a[2][3]={{1,2,3},{4,5,6}};
在这里插入图片描述

第三章 函数与指针

3.1、函数

1、无返回值函数(void函数)

void printMessage() {
    printf("Hello, World!\n");
}

2、有返回值函数

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

3、函数原型(函数声明)

int add(int a, int b); // 函数原型

4、递归函数
递归函数是指能够调用自身的函数。使用递归可以解决那些可以被分解成较小、相似形式的问题

int factorial(int n) {
    if (n == 0) {
        return 1;
    } else {
        return n * factorial(n - 1);
    }
}

5、内置函数(标准库函数)

printf()、scanf()

6、预处理器函数(宏函数)

#define MAX(a, b) ((a) > (b) ? (a) : (b))
int maxNum = MAX(10, 5); // 宏替换为最大值的判断表达式:((10) > (5) ? (10) : (5))

7、内联函数(inline函数)

inline int myfunc(int a, int b)
{
    return a < b ? a : b;
}

内联函数\宏代码片段都是为减少而外开销
内联函数由 编译器处理,直接将编译后的函数体插入调用的地方
宏代码片段 由预处理器处理, 进行简单的文本替换,没有任何编译过程
用法区分案例

#include <stdio.h>


#define MYFUNC(a, b) ((a) < (b) ? (a) : (b))

int myfunc(int a, int b);
// 定义一个内联函数
inline int myfunc(int a, int b)
{
    return a < b ? a : b;
}

int main() {
        int a = 1;
        int b = 3;
       int c = myfunc(++a, b);
       // int c = MYFUNC(++a, b);

        printf("a = %d\n", a);
        printf("b = %d\n", b);
        printf("c = %d\n", c);


    return 0;
}

函数调用模型
在这里插入图片描述

3.2、指针

3.2.1、指针是什么?

指针是用来存放地址的,地址是唯一标示一块地址空间的
一个房间的门口门牌号:2008,2008地址就是指针,这个门牌号的房间存储人就是它存储的变量

int main() {
    int a = 100//定义整型变量a,b并初始化(开辟内存空间存储10)
    int* p1;     //定义指向整型数据的指针变量p1;
    p1 = &a;          //把变量a的地址赋给指针变量p1
    printf("a=%d\n", a);//输出变量a值
    printf("*p1=%d\n", *p1);

}

指针 &a(0xfff)
指针就是地址,地址就是指针。

指针变量 p1
指针变量是变量。定义一个指针变量,是在内存中开辟一个空间,该空间里面存放地址

在这里插入图片描述

3.2.2、指针是一种数据类型

1、指针初始化(两步骤)与获取指针指向变量值:

int nums=10;//定义一个变量
int *p = &nums;//定一个指针变量指向内存地址&nums
int value = *p;//访问指针所指向的值
*p=12;
int *ptr=NULL;//空指针NULL

1)两码事:指针变量和它指向的内存块变量
2)条件反射:指针指向某个变量,就是把某个变量地址否给指针
*p放在等号的左边赋值(给内存赋值)
*p放在等号的右边取值(从内存获取值)

2、指针变量和它指向的内存块是两个不同的概念
//含义1 给p赋值p=0x1111; 只会改变指针变量值,不会改变所指的内容;p = p +1; //p++
//含义2 给p赋值p=1; 不会改变指针变量的值,只会改变所指的内存块的值
//含义3 =左边p 表示 给内存赋值, =右边p 表示取值 含义不同切结!
//含义4 =左边int*p
//含义5 保证所指的内存块能修改

3、多级指针

#include <stdio.h>

int main() {
    int num = 10;
    int *ptr1 = &num; // 一级指针
    int **ptr2 = &ptr1; // 二级指针

    printf("Value: %d\n", **ptr2); // 解引用操作
    printf("Address: %p\n", *ptr2); // 一级指针地址
    printf("Double Address: %p\n", ptr2); // 二级指针地址

    return 0;
}
3.2.3、间接赋值指针存在的最大意义

2、间接赋值、直接赋值
*p间接赋值成立条件:3个条件
a)2个变量(通常一个实参,一个形参)
b) 建立关系,实参取地址赋给形参指针
c) *p形参去间接修改实参的值

int nums = 0; //实参
int *p = &nums;
nums = 1;//直接赋值
*p =2 ; //通过*形参 == 间接地改变实参的值
*p成立的三个条件:

间接赋值


int *p = &nums;
nums = 1;//直接赋值
上面两部放函数实现
int showbuf(int *p)

理解指针必须和内存四区概念相结合
1)主调函数 被调函数
a)主调函数可把堆区、栈区、全局数据内存地址传给被调用函数
b)被调用函数只能返回堆区、全局数据
2)内存分配方式
a)指针做函数参数,是有输入和输出特性的。

在这里插入图片描述

3.2.4、指针与数组

1、一级指针与一维数组
int a[]={3,2,1}; int *p = a
知识点:1、a的类型为int[2],p的类型为int *
2、a=数组的首地址,p指向数组首地址

a = &a[0] = &a 只是地址相等 意义不一样
&a是一个数组指针 其类型为 int (*)[2];
a+1 相当于 a的地址+sizeof(int)*1
&a+1 相当于 a的地址 + sizeof(int [2])*1

	int a[]={3,2,1};
	int *p = a;
	printf("p = %d\n",p);
	printf("&a = %d\n",&a);
	printf("a = %d\n",a);
	printf("&a[0] = %d\n",&a[0]);
	printf("&p = %d\n\n",&p);
	
	printf("*p = %d\n",*p);
	printf("a[0] = %d\n",a[0]);

    int a[]={1,2,3};
	int *p = a;
	printf("*(p+1) = %d\t",*(p+1));
	printf("p[1] = %d\n",p[1]);
	printf("*(a+1) = %d\t",*(a+1));
	printf("a[1] = %d\n\n",a[1]);
	
	printf("p = %d\n",p);
	printf("p+1 = %d\n",p+1);
	printf("&p[1] = %d\n",&p[1]);
	
	printf("a+1 = %d\n",a+1);
	printf("&a[1] = %d\n",&a[1]);

指针多维数组
指针数组:
这里就要先说明和[]的优先级了,[]的优先级是高于的,所以int *p[3];等同于int *(p[3]);。所以它是一个指针数组

#include <stdio.h>

int main() {
    int num1 = 10;
    int num2 = 20;
    int num3 = 30;

    int *p[3] = {&num1, &num2, &num3}; // 指针数组存储整型数据的指针

    for (int i = 0; i < 3; i++) {
        printf("Value: %d\n", *(p[i])); // 解引用指针获取整型数据的值
        printf("Address: %p\n", p[i]); // 打印指针地址
        printf("\n");
    }

    return 0;
}

数组指针:
int a[3][4] = {{0, 1, 2, 3}, {4, 5, 6, 7}, {8, 9, 10, 11}};
int (*p)[4];
a[0]、a[1]、a[3]可以在二维数据拆为每个分别有3个参数一维数组首地址

#include <stdio.h>
int main()
{
    int a[3][4] = {{0, 1, 2, 3}, {4, 5, 6, 7}, {8, 9, 10, 11}};
    int(*p)[4];
    p = a;
    printf("%d\n", sizeof(*(p + 1)));
    return (0);
}

a[i][j] = *(p[i]+j) = ( (p+i)+j)
a[1][0]的值 ((p + 1))
a[1][1]的值
(
(p + 1)+1)

	int a[2][3]={1,2,3,4,5,6};
		printf("一维数组:\n");
		printf("a = %d\n",a);
		printf("*a = %d\n",*a);
		printf("&a = %d\n",&a);
		printf("a[0] = %d\n",a[0]);
		printf("&a[0][0] = %d\n\n",&a[0][0]);
		
		printf("二维数组:\n");
		printf("a[1][2] = %d\n",a[1][2]);
		printf("*((a[1])+2) = %d\n",*((a[1])+2));
		printf("*(*(a+1)+2) = %d\n",*(*(a+1)+2));

在这里插入图片描述

int arr[3][3] = {{1, 2, 3}, {4, 5, 6}, {7, 8, 9}};
int (*ptr)[3] = arr; // 定义一个指向整型数组的指针,指向二维数组arr

for (int i = 0; i < 3; i++) {
    for (int j = 0; j < 3; j++) {
        printf("%d ", *(*(ptr + i) + j)); // 使用指针访问二维数组元素
    }
    printf("\n");
}

int (*p)[4];是数组指针,它指向二维数组中每个一维数组的类型,它指向的是一个数组。

int *p[4];是指针数组,它是一个数组,数组中每个数是指向int型的指针

3.2.5、指针、数组与函数

一级指针典型用法(指针做函数参数)
int showbuf(char *p)
int showArray(int *array, int iNum)

void showArray(int[]array, int iNum) {
    for (int i = 0; i < iNum; i++) {
        printf("%d ", array[i]);
    }
    printf("\n");
}

void showArray(int *array, int iNum) {
    for (int i = 0; i < iNum; i++) {
        printf("%d ", array[i]);
    }
    printf("\n");
}

malloc 和 free动态分配和释放内存

malloc\free

一级指针做输入
int showbuf(char *p)
int showArray(int *array, int iNum)
一级指针做输出
int goLen(char *pName, int *len);
理解
主调函数还是被调用函数分配内存
被调用函数是在heap/stack上分配内存

二级指针典型用法(指针做函数参数)
二级指针做输入
int main(int arc ,char *arg[]); 字符串数组
int shouMatrix(int [3][4], int iLine);
二级指针做输出
int GetName(char **name);

野指针的避免

  1. 指针初始化
  2. 小心指针越界
  3. 指针指向空间释放即使置NULL
  4. 避免返回局部变量的地址
  5. 指针使用之前检查有效性
//指针初始化
#include<stdio.h>
int main()
{
	int* p;                       
	*p = 10;  
	
	return 0;
}

//指针越界
int main()
{
	int arr[10] = { 0 };
	int i = 0;
	int* p = arr;
	for (i = 0; i <= 10; i++)
	{
		*p = i;                   //i=10时越界
	}
	return 0;


//返回局部变量的地址
int* test()
{
	int a = 10;
	return &a;
}

//指针使用之前检查有效性
int showbuf(char *p)
{
	if(p==NULL){
    }
}

第四章 C语言扩展

4.1、const、static专题

int main()
{
const int a;  //
int const b; 
const char *c;
char * const d; char buf[100]
const char * const  e ;

return 0;
}

int goFun(const )

初级理解:const是定义常量----->const意味着只读

含义:
//第一个第二个意思一样 代表一个常整形数
//第三个 c是一个指向常整形数的指针(所指向的内存数据不能被修改,但是本身可以修改)
//第四个 d 常指针(指针变量不能被修改,但是它所指向内存空间可以被修改)
//第五个 e一个指向常整形的常指针(指针和它所指向的内存空间,均不能被修改)

//合理的利用const,
//1指针做函数参数,可以有效的提高代码可读性,减少bug;
//2清楚的分清参数的输入和输出特性

结论:
//指针变量和它所指向的内存空间变量,是两个不同的概念。。。。。。
//看const 是放在*的左边还是右边 看const是修饰指针变量,还是修饰所指向的内存空变量

在C语言中,static是一个关键字,用于修饰变量、函数和数据类型
1、静态局部变量

void myFunction() {
    static int count = 0; // 静态局部变量
    count++;
    printf("Count: %d\n", count);
}

int main() {
    myFunction(); // 输出:Count: 1
    myFunction(); // 输出:Count: 2
    return 0;
}

2、静态全局变量、静态全局变量


#include <stdio.h>

static int count = 0; // 静态全局变量

void increment() {
    count++;
}
static void myFunction() { // 静态函数
    printf("Hello, World!\n");
}

void display() {
    printf("Count: %d\n", count);
}

int main() {
    increment();
    display(); // 输出:Count: 1
    return 0;
}

4.2、结构体

结构体类型定义及结构体变量定义
结构体是一种构造数据类型
用途:把不同类型的数据组合成一个整体-------自定义数据类型
结构体类型定义


声明一个结构体类型
struct _Teacher
{
	char	name[64];//64
	char	tile[32];
	int		age; //4
	char	addr[128];
};

struct _Student {
    char name[32];
    char title[32];
    int age;
    char addr[128];
} s1, s2;//定义类型的同时,定义变量;

typedef struct _Teacher
{
	char	name[64];//64
	char	tile[32];
	int		age; //4
	char	addr[128];

}AdvTeacher;


//初始化结构体变量的几种方法
//1)
struct _Teacher t4 = {"name2", "tile2", 2, "addr2"};
//2)
struct Dog1
{
	char	name[32];
	char	tile[32];
	int		age;
	char	addr[128];
}d5 = {"dog", "gongzhu", 1, "ddd"};

 // 定义匿名结构体类型并创建结构体变量
    struct {
        char name[32];
        char title[32];
        int age;
        char addr[128];
    } s3;

    // 给结构体成员赋值
    strcpy(s3.name, "John");
    strcpy(s3.title, "Mr.");
    s3.age = 20;
    strcpy(s3.addr, "123 Main St");

// 创建指向结构体变量的指针
    struct {
        char name[32];
        char title[32];
        int age;
        char addr[128];
    } *ptr_s3;

  // 给结构体成员赋值通过指针访问
    strcpy(ptr_s3->name, "John");
    strcpy(ptr_s3->title, "Mr.");
    ptr_s3->age = 20;
    strcpy(ptr_s3->addr, "123 Main St");

4.3、拷贝、文件、线程

4.4.1、什么是浅拷贝和深拷贝

浅拷贝就是直接赋值
深拷贝就是把一个内存值拷贝到另外一个内存,相互不影响

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

typedef struct Teacher
{
	char name[64];
	int age ;
	char *pname2;
	
}Teacher;

//如果 想执行深copy,再显示的分配内存
void copyTeacher(Teacher *to, Teacher *from)
{
	*to = *from;

	to->pname2 = (char *)malloc(100);
	strcpy(to->pname2, from->pname2);

	//memcpy(to, from , sizeof(Teacher));
}
void main51()
{
	Teacher t1;
	Teacher t2;

	strcpy(t1.name, "name1");
	t1.pname2 = (char *)malloc(100);
	strcpy(t1.pname2, "ssss");

	//t1 copy t2

	copyTeacher(&t2, &t1);

	if (t1.pname2 != NULL)
	{
		free(t1.pname2);
		t1.pname2 = NULL;
	}

	if (t2.pname2 != NULL)
	{
		free(t2.pname2);
		t2.pname2 = NULL;
	}

	printf("hello...\n");
	system("pause");
	return ;
}
4.4.2 FILE文件操作

1、常用的函数有fopen、fclose、fread、fwrite

4.4.3 Thread线程

1、定义pthread_t
2、创建方法 :void *Fun(void *arg);
3、创建pthread_create
4、等待线程结束 pthread_join

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

#define NUM_THREADS 5

// 线程函数
void *printHello(void *threadID) {
    long tid = (long)threadID;
    printf("Hello from thread %ld\n", tid);
    pthread_exit(NULL);
}

int main() {
    pthread_t threads[NUM_THREADS];
    int rc;
    long t;

    // 创建多个线程
    for (t = 0; t < NUM_THREADS; t++) {
        printf("Creating thread %ld\n", t);
        rc = pthread_create(&threads[t], NULL, printHello, (void *)t);
        if (rc) {
            printf("Error creating thread: %d\n", rc);
            exit(-1);
        }
    }

    // 等待所有线程完成
    for (t = 0; t < NUM_THREADS; t++) {
        pthread_join(threads[t], NULL);
    }

    printf("All threads have finished execution.\n");

    return 0;
}

4.4 正规接口规范

4.4.1接口

1、提供头文件h对应方法申明
2、c文件方法实现

4.4.2函数扩展

1、指针函数

<数据类型>  *<函数名称>(<参数说明>)
	{
	      语句序列;
	}

2、函数指针

<数据类型> (*<函数指针名称>)(<参数说明列表>);

int test(int a, int b, int (*pFunc)(int,int));

定义函数指针类型

typedef <数据类型> (*<函数指针类型名称>)(<参数说明列表>);

typedef int (*MFunc)(int, int);
  • 4
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
信息数据从传统到当代,是一直在变革当中,突如其来的互联网让传统的信息管理看到了革命性的曙光,因为传统信息管理从时效性,还是安全性,还是可操作性等各个方面来讲,遇到了互联网时代才发现能补上自古以来的短板,有效的提升管理的效率和业务水平。传统的管理模式,时间越久管理的内容越多,也需要更多的人来对数据进行整理,并且数据的汇总查询方面效率也是极其的低下,并且数据安全方面永远不会保证安全性能。结合数据内容管理的种种缺点,在互联网时代都可以得到有效的补充。结合先进的互联网技术,开发符合需求的软件,让数据内容管理不管是从录入的及时性,查看的及时性还是汇总分析的及时性,都能让正确率达到最高,管理更加的科学和便捷。本次开发的高校科研信息管理系统实现了操作日志管理、字典管理、反馈管理、公告管理、科研成果管理、科研项目管理、通知管理、学术活动管理、学院部门管理、科研人员管理、管理员管理等功能。系统用到了关系型数据库中王者MySql作为系统的数据库,有效的对数据进行安全的存储,有效的备份,对数据可靠性方面得到了保证。并且程序也具备程序需求的所有功能,使得操作性还是安全性都大大提高,让高校科研信息管理系统更能从理念走到现实,确确实实的让人们提升信息处理效率。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

baoyu45585

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值