C语言从入门到项目实战学习笔记

本文是《C语言从入门到项目实战》的学习笔记,涵盖C语言的基础概念、数据类型、程序设计思想、循环结构、函数、数组、指针、结构体、文件操作等内容,深入解析C语言的特性和实践技巧,包括预处理、函数声明、数组初始化、指针操作、内存管理等。此外,还讨论了结构体和文件操作,为后续的项目实践打下基础。
摘要由CSDN通过智能技术生成

概述

这里针对《C语言从入门到项目实战》整理出C的学习笔记,以备以后知识点复习,顺便给入门的小伙伴一个学习参考。

C语言特性

环境搭建:[https://blog.csdn.net/helloworld9998/article/details/103596018]

输入输出
多文件编译两种方法
linux 下include 包含的文件在 /usr/include 中
windows 下 include文件和编辑器有关,一般都在编辑器的目录下,比如codeblocks在Mingw的include。
在C盘下搜索一个系统函数如“stdio.h”,就会知道它的路径了
常用的库stdio.h windows.h unistd.h stdlib.h conio.h system函数
连接编译 比如使用了windows下的winmm库,也就是winmm.dll,编译需要在命令中使用gcc xx.c -o xx.exe -lwinmm

为windwos 查找文件
dir c:\winmm.dll /s /b 在c盘中找winmm。dll
/s 显示指定目录和所有子目录中搜索匹配。
/b 不会显示标题和盘符信息等

在内容中查找
find /N /I “system” a.c
/N 显示行号
/I 忽略大小写
/V 不包含内容的其他信息,反向查找

删除隐藏文件
attrib 查看文件属性
del /ah 文件名
或者更改属性,再删除
具体:
https://blog.csdn.net/dodott/article/details/81183801

数据类型与运算

%d,%o %x %X 十六进制大写 带有进制标识 %#o,%#x,%#X
long 整形形使用%ld long long占8个字节 ,使用%lld
bool型变量,在stdbool.h头文件,直接使用 bool a =true, a的值其实就是1。
char 型字符型,其实在也一个整数类型,使用时注意要用单引号。char a = ‘a’
int x= ‘ABCD’ printf("%x",x) -----输出41424344 把4个独立字符放在一个32位int类型变量中。

浮点型变量 输出用 %f 科学计数法%e double e=234.123e9,long double型使用%Lf,%Le输出。
double 数据输入输出使用%lf,但一般在输出时使用%.5f,来保留一定位数的小数。
数据类型的隐式转换,int double 取模必须为整数
使用++ – 提高执行效率,使用一条机器指令,而i=i+1。需要三条指令。

运算符优先级

&& 和 || 是逻辑运算符的短路,只要左边的值解决整个计算结果,不进行右边计算。
!x 如果x为0 值为1,否则为0
K || i++ && j-3, 即使 k成立,不会计算i++,但是会计算j-3
sizeof 结果为size_t 类型,定义在stddef.h及其他标准头文件被定义为无符号整数类型。
三元运算 表达式1 ? 表达式2 : 表达式3 如果表达式计算完不等于0 ,执行表达式2,等于0 ,执行表达式3。

简单程序设计思想

数据结构+算法=程序
数据结构:处理对象(数据)如何组织
算法:做什么,怎么做,解决问题的方法和步骤。

多读算法和书,有助于解决问题,写出好的算法,好的程序。

算法五要素:
输入 输出 有穷性 确定性 可行性

流程图表示法:
起止框 输入输出框 处理框 判断框

switch 语句中
判断表达式为一个值, case和default次序任意,多个case可以公用一个语句,每个语句后有break,否则会直接执行后面case语句,直到找到break,或者switch右括号,多有多个语句时,不需要使用大括号括起来。

程序结构之循环

do while 循环是循环体总要被执行一次。循环次数已知,最好用for循环。
for 循环中变量的作用域在for的大括号中。
推断罪犯问题: p98页;p103问题抽象为代码实现。
打印肥波那契数列打印前50。

函数

函数名代表了函数对应代码在内存中存储首地址
建议定义函数时,避免函数内变量名和函数名重复
在程序中调用函数时,如果函数未定义,即该函数的定义在调用处后面,这时需要对函数进行声明,声明目的在于告诉编译器函数名是什么?形式参数有几个,形参类型,返回值类型。这样编译器遇到调用该函数时,会对上面几项进行检查,如果函数定义在调用处前面则不需要声明。

一般建议在函数之前声明函数,在其他文件定义和函数。
返回类型 函数名(形参类型,形参类型…)
形参一般值传递,如果形参为指针,则指向实参地址,改变的是实参的值。
递归就是函数调用自己。找出递归关系,要有递归出口。
全局变量和静态局部变量(static) de 生存周期为程序的整个周期,直到程序从内存中退出。
全局变量就是任何函数之外的变量。当全局变量和局部变量重名时,在局部变量作用域内访问的是局部变量。
如果在其他文件应用全局变量,需要在使用的函数中或者其他文件中用extern关键字修饰扩展全局变量作用域,如果全局变量或者函数用static修饰,则为静态变量或者函数,只能在本文件内使用,不能被其他文件所调用。
静态局部变量作用域为函数内部,变量分配在全局数据区全局变量和静态局部变量未赋初始值时,编译器会自动初始化为0。

C语言由源代码生成可执行文件过程:
C源程序->预处理->编译->优化程序->汇编程序->链接程序->可执行文件。其中把预处理,编译,优化,汇编统称为汇编阶段。
预处理指令分为4类:包含头文件,宏定义及宏展开,条件编译,特殊符号处理。
预处理指令不能用分号作为结束符。
#include <aaa> 预编译编译在编译器自带的或者外部库查找头文件
#include “xxx” 现在当前文件目录查找,找不到再去编译器自带头文件查找。
宏:
#define 标识符 特定字符串
1,无分号
2,字符串可是数字,字符串,函数。
3,如果为函数,记得把函数的语句用括号括起来。
#define VERSION “Version 1.0 copyright© 2019”
#define PI 3.1415
#define PRINT(x) printf("%d",x)
#define CUBE() (x*x**x)

#define
#undef 取消宏定义
条件编译
#if 检测后面常量表达式,直到出现#else #elif #endif为止,否则不编译
#endif 终止#if预处理指令

#ifdef 常用来检测宏是否定义。
#else ,用在#if后,if的不满足,就编译else后面的代码
#elif 嵌套条件编译
#elifdef
#endif

#ifdef 等价于 #if defined
#ifndef等价于 #if !defined,

预处理特殊符号:

__FILE__   包含当前文件名的字符串
__TIME__
__DATE__
__LINE__   当前行号

#error
#error Compile Version is not provided! 使编译显示一条错误,并停止编译

#pragma pack——用于指定内存对齐方式 ,#error #line #pragma使用具体参考如下
https://www.cnblogs.com/CoderTian/p/5903280.html

模块化编译连接:
多文件编译时,
把每个文件编译成二进制代码:
gcc -c xxx.c -o xxx.o
再把所有。o链接为可执行程序
gcc xxx.o xx.o -o xx.exe
其中.o文件不可执行,因为还没有把诸如printf()等这些库函数的二进制实现代码链接起来。

源码编译进程序
gcc -g xxx.c -o xxx.exe

真的会数组么

数组:用来存放一组相同类型数据的数据类型。数组名是一个常量,表示第一个元素地址,不能出现在括号左边。
比如: char a[100]; a=“aaaa”;编译的时候是错误的。

数组最低地址对应第一个元素,最高地址对应最后一个元素。
系统为数组申请 数组长度*sizeof(元素类型)个连续的内存单元,数组名为数组首地址,比如

int test[10] test 指向第一个元素,test+1 指向第二个元素,*(test+1) 等价于 test[1],在内存中,第二个元素的地址为第一个元素加上int类型占用的4个字节。

数组的初始化 int a[10] ={0},所有值为0。int b[]={1,2,3,4]
也可以用sizeof(test)/sizeof(int) 求数组总长度,如果你初始化了5个元素,前面结果还是10。

int a[5]={0};
int b[5}={1};
a=b;
是错误的,不可以对两个数组直接进行赋值,因为阿a代表数组第一个元素地址,是一个常量,不可出现在左边。
如果是字符数组,char str1[]=“asdfasdfasdfasfd” 长度为sizeof(str1)/1,结果还是sizeof(str1).
冒泡排序:

c语言不对数据形参下标边界检查,通常还需要传递一个表示数组大小的值。
二维数组元素在内存中是连续存放的,按行优先存储,比如int a[3][4] 先放a[0] 行,在放a[1],然后a[2],元素都是连续存放,设一个数组为a[M][N] ,则a[i][j] 的地址为 a + (i*N+j)*4
赋值的时候,
int a[4][3] ={ {1,2,3,},{4,5,6},{7,8,9},{11,12,13}}
int a[4][3]={1,2,3,4,5,6,7,8,9,10,11,12}
int a[4][3] = { {1},{2},{3}} 只对每一行第一个元素赋值
如果对全部元素赋值,则一维长度可以不写
int a[][4] = {1,2,3,4,5,6,7,8,9,10,11,12}

二维数组作为函数的形参的几种定义形式

方法一: 形参给出第二维的长度。
类型 数组名[一维长度] [二维长度]
类型 数组名 [] [二维长度]

方法二:形参声明为指向数组的指针。
类型  (*数组名)[二维长度]

方法三:形参声明为指针的指针。
类型 ** 数组名
这种形式,不能使用如array[i][j] 对数组取赋值,应该使用array[i*j +j],
看作为一个一维数组进行访问,总长度为i*j。

字符串使用字符数组来存储,在如果字符串有N个字符,则需要长度N+1个字符数组存储,因为末尾需要加“\0"
在定义时进行初始化,不可以往数组名赋值,因为数组名是常量。
数组输入:
如果数组中没有空格:

char str1[100];
scanf("%s",str1);

如果有空格:

#include <string.h>
gets(str1);

输出:

printf(“%s",str1);
or
puts(str1);

字符串操作:在string.h中添加链接描述

char * strcat(cahr * dest,const char* src),将src附加到dest,返回dest
char * strcpy(char * dest, const char* src) ,将src拷贝到dest,返回dest
int  strcmp(const char * str1,const char* str2),
比较str1,str2,相等返回0,前者大,返回正数,后者大返回负数。

终极进化之指针

指针就是内存地址,指针变量就是存储地址的变量

指针变量定义:
数据类型 * 变量名 [=初值]
数据类型是指针变量存储的值的类型
&取址符 ,*取值符 。
指针变量空间大小,sizeof(指针) ==4;表示4个字节。
指针变量多用于函数形参,通过传递实参地址给形参,达到修改变量目的。

定义指针时,最好把指针赋值为NULL

指针,指针变量和变量,一维数组,二维数组,结构体在内存中的分布详细探讨:
https://www.cnblogs.com/souhaite/p/10929781.html
http://c.biancheng.net/view/246.html
https://blog.csdn.net/qq_39883358/article/details/86765695
https://blog.csdn.net/constantin_/article/details/79575638
https://www.jianshu.com/p/5f2396ae5466

memset()使用:
void * memset(void *s,int c,size_t n);
常用来对数组内容清零,
比如: 
从a的起始地址开始,把0填充到 10个int类型内存空间,也就是40个字节。
int a[10];
memset(a,0,10*sizeof(int));

void * 指针为万能指针,可以通过强制转换为任何其他类型指针。在使用时,必须要把void *型指针强制转换为具体类型,比如:

    int b =6 ;
    void *a=&b;
    printf("%d\n",*(int *)a);
    //*a是不合法的,因为编译器不知道void指针存储的类型是什么
    //强制转换为(int *) int型,编译器知道指针存储的数据类型为int
    //典型例子为malloc函数返回void 类型,所以必须强制转换

动态内存分配:

malloc()使用:
void * malloc(size_t size);
返回被分配内存的地址,初始值不确定,否则返回NULL,
使用完应该使用free释放掉,操作系统不会自动回收动态分配的内存。
void free(void *ptr)

例子:

void my_dy_array()
{
   
    int n;
    int sum =0;
    printf("Please input array length\n");
    scanf("%d",&n);
    int * array=NULL;
      array = (int *) malloc(n*sizeof(int))  ;
      memset(array,0,n*sizeof(int));
      printf("Please input %d numbers\n",n);
      for(int i =0;i<n;i++)
      {
   
          scanf("%d",array+i);
          sum+=*(array+i);

      }
      printf("sum ==%d\n",sum);

      free(array);
}

输出:

Please input array length
5
Please input 5 numbers
11 12 13 14 15
sum ==65

动态分配内存时,存放字符串的末尾需要加上’\0’,注意是单引号。
例子:

void rand_str()
{
   
    int len;
    printf("Please input length of str\n");
    scanf("%d",&len);
    char *buffer=NULL;
    buffer = (char * )malloc(len+1);
    srand(time(0));
    if(buffer==NULL)
    {
   
        exit(1);
    }
    memset(buffer,'a',len+1);
    for(int i =0;i<len;i++)
    {
   
       buffer[i]=rand( )% 26+'a';
    }
  buffer[len]='\0';
    printf("%s\n",buffer);
   // printf("%d",rand());
    free(buffer);
}

输出:

Please input length of str
10
pexxhblpdy

const指针常量:
1,当使用const 修饰 * 时,表示这个指针指的数据内容不能修改,但是指针的地址可以改。多用在函数形参中,保证指针的值不被修改。

    int a =5,b=6;
    int const *p=&a;
    *p=b;//wrong
    p=&b;

2, 当使用const 修饰 指针名时,表示这个指针的地址不能改变,但是指针所指数据内容可以改

    int a =5,b=6;
    int * const p2=&a;
    p2=&b;// wrong
    *p2 =9;

3,指针常量即是常量指针,又是常量指针变量。

    int const * const p3=&a;
    *p3 =0;//wrong
    p3=&b;//wrong

指针访问一维数组,数组名就是指向数组首地址的指针,数组名++,表示指向下一个元素地址。

void my_array_p()
{
   
    int a[5]={
   1,2,3,4,5};
    int *p=a;
    p[2] =333;
    *(p+3) = 444;
    printf("addres of  a ==%p\n",a);
    for(int i = 0;i<5;i++)
    {
   
        printf("a[%d] address ==%p, a[%d]==%d\n",i,p+i,i,*(p+i));
        //%p以十六进制整数方式输出指针地址,用%x ,%X, %#X 都可以。
        // printf("a[%d] address ==%p, a[%d]==%d\n",i,p,i,*(p));
         //p++;
    }
}

输出:

addres of  a ==0060FEB4
a[0] address ==0060FEB4, a[0]==1
a
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值