C语言编程
发布日期:2021-06-24 07:05:17 浏览次数:4 分类:技术文章

本文共 10150 字,大约阅读时间需要 33 分钟。

C简介

一、基本概念和特点

1、基本概念:C 是一门 面向过程 强类型 静态 编译型 高级语言

2、C语言的特点如下:

  • C语言是一个有结构化程序设计、具有变量作用域以及递归功能的过程式语言;
  • C语言传递参数均是以值传递,另外也可以传递指针;
  • 不同的变量类型可以用结构体组合在一起;
  • 部份的变量类型可以转换,例如整型和字符型变量;
  • 通过指针,C语言可以容易的对存储器进行低级控制;
  • 预编译处理让C语言的编译更具有弹性;

二、hello world

#include 
int main(){ /* 我的第一个 C 程序 */ printf("Hello, World! \n"); return 0;}

*******************tips***********************

  • 编译 C 程序请先安装 GCC 编译器;

 

基础知识

一、基本语法

  • C 的令牌(Tokens)是最小词法/语法元素,可以是关键字、标识符、常量、字符串值,或者是一个符号;
  • C语句也是以分号结束,且分号不能少;
  • 注释和 java 一样(单行和多行);
  • 标识符命名规则也和 java 一致;
  • C语言对大小写敏感;

二、数据类型

1、整数类型

  • char-----unsigned char(1)
  • short-----unsigned short(2)
  • int-----unsigned int(4)
  • long-----unsigned long(8)
  • long int------long long

2、浮点类型:float(4)----double(8)----long double(16)

3、void 类型

  • 函数的返回值和参数为空都可以用 void;
  • 指针指向 void:类型为 void * 的指针代表对象的地址,而不是类型。例如,内存分配函数 void *malloc( size_t size ); 返回指向 void 的指针,可以转换为任何数据类型。

4、类型转换

  •  自动转换(隐式转换),基本的方向如下:

  • 有时候需要进行强制转换,下面的例子是否进行强制转换得到的结果是不同的
#include 
int main(){ int sum = 17, count = 5; double mean; mean = (double)sum / count; printf("Value of mean : %f\n", mean ); // result is 3.4 mean = sum / count; printf("Value of mean : %f\n", mean ); //result is 3.0}

 

三、变量、常量、存储类和运算符

1、变量

(1)变量的申明和定义

extern int i; //声明,不是定义int i; //声明,也是定义
  • 定义是需要分配存储空间的,而仅仅申明不需要;

(2)关于左值和右值

  • 左值:指向内存位置的表达式被称为左值(lvalue)表达式;
  • 右值:指的是存储在内存中某些地址的数值;
  • 总结:变量是左值,可以出现在等号的左右两边;数字是右值,不能出现在赋值号的左边;

2、常量

(1)四种常量

  • 整数常量和浮点常量:
  • 字符常量和字符串常量(区分单括号和双括号):转义字符的用法和其他的语言差不多;

(2)常量定义

#define LENGTH 10   const int  WIDTH  = 5;

3、存储类(类似于 java 中的修饰符)

  • auto:局部变量默认的存储类,只能修饰局部变量;
  • register:修饰的还是局部变量,但是这个局部变量比较特殊,一般是存在寄存器(CPU)中的,不能使用一元的 & 运算符(不在内存中);被 register 修饰不一定就是存在寄存器,而是可能存在寄存器(受硬件条件影响)
  • static:修饰局部变量,在程序的整个生命周期这个局部变量都不会销毁;修饰全局变量,会使变量的作用域限制在声明它的文件内(在另外的文件中即使用 extern 也无法访问),只要在同一个文件中怎么调用都行;
  • extern:是用来在另一个文件中声明一个全局变量或函数(引用其他文件的非 static 的全局变量和函数);

4、运算符

  • 算数运算符也是支持 ++ 和 -- 的,用法和 java 一样;
  • 位移运算比 java 少了无符号的右移,其余一致;
  • 其他运算符:sizeof     &取地址     *a 指向一个变量;

四、语句和函数

1、语句

  • 判断:if---else 和 switch(break),用法和 java 基本是一样;
  • 循环:while、for 和 do--while,用法也是和 java 一致的,也支持continue 和 break,还有goto(跳转到标记的行,不建议使用);

2、函数

  • 函数声明:int max(int num1, int num2);使用在前面,定义在后面的话,需要在使用之前进行函数的声明;
  • 函数传参:值传递(不会改变实参的值)和引用传递(通过指针传递方式,形参为指向实参地址的指针,当对形参的指向操作时,就相当于对实参本身进行的操作);

3、作用域

  • 全局变量:全局变量保存在内存的全局存储区中,占用静态的存储单元;自动初始化;
  • 局部变量:局部变量保存在栈中,只有在所在函数被调用时才动态地为变量分配存储单元;需要手动初始化;
  • 函数形参:被当作该函数的局部变量;

 

数据类型(扩展)

一、数组

1、基本使用

  • 数组的声明和初始化如下,访问还是通过下标访问;
  • 数组容量的获取可以使用:double haha[4];int len  = sizeof(haha)/sizeof(double);
  • 多维数组的使用和 java 没有太大的差异;
  • 数组名 balance 和 &balance 的值是一样的,但意义不同,前者是数组的首元素地址,后者是整个数组的地址;一般情况等同来用不会有太大的问题;
double balance[5] = {
1000.0, 2.0, 3.4, 7.0, 50.0};double balance[] = {
1000.0, 2.0, 3.4, 7.0, 50.0};

2、数组作函数参数和返回值

 

  • 返回数组一定要将函数内的数组声明为 static;不允许返回一个完整的数组,只能返回一个指针(数组名),函数的定义的时候返回值也要写指针;
  • 类似指针作形参 void myFunction(int *param),可以传递一个数组(这里的变量名 param 和数组名实际代表的都是地址,可以等同看待);

************************tips*****************************

  • C语言随机数使用:srand( (unsigned)time( NULL ) );rand();
  • 计算机内存的基本单位是 byte,而不是 bit;

 3、指向数组的指针和指针数组

(1)指向数组的指针

  • 在如下定义的前提下,*(balance + 4) 是一种访问 balance[4] 数据的合法方式
double *p;double balance[10];p = balance;

(2)指针数组

  • 首先这是一个数组;
  • 其次这个数组的元素都是指针,存的是指向其他变量的地址;
  • 可以用一个指向字符的指针数组来存储一个字符串列表,使用如下:
char *names[] = {               "Zara Ali",               "Hina Ali",               "Nuha Ali",               "Sara Ali",};

(3)指向指针的指针

  • 多级的间接寻址的方式,会形成指针链
int  var;int  *ptr;int  **pptr;var = 3000;ptr = &var;pptr = &ptr;printf("Value of var = %d\n", var );printf("Value available at *ptr = %d\n", *ptr );printf("Value available at **pptr = %d\n", **pptr);

二、枚举

 1、枚举的定义和使用

  • 枚举的本质就是一些离散的整数值;
  • 枚举的定义和使用如下所示:
enum DAY{      MON=1, TUE, WED, THU, FRI, SAT, SUN};enum DAY day;

2、其他说明

  • 枚举的遍历:值连续的枚举可以循环遍历;
  • 整数转换为枚举类型:(enum DAY)1;
  • 枚举在 switch 语句中的应用(java 中也可以);

三、指针

1、指针是什么:指针是一个特殊的变量,这个变量的值是另一个变量的地址

2、指针怎么用

#include 
int main (){ int var = 20; /* 实际变量的声明 */ int *ip; /* 指针变量的声明 */ ip = &var; /* 在指针变量中存储 var 的地址 */ printf("Address of var variable: %p\n", &var ); /* 在指针变量中存储的地址 */ printf("Address stored in ip variable: %p\n", ip ); /* 使用指针访问值 */ printf("Value of *ip variable: %d\n", *ip ); return 0;}

3、指针使用细节

  • 指针的递增递减和比较:指针支持 ++ -- 等算数操作和 <> 等比较运算;
  • 函数指针:指向函数的指针,给函数指针赋值时,可以用&fun或直接用函数名fun,二者基本是等效的,都能取到函数的地址;
  • 回调函数:()
    • 函数指针变量可以作为某个函数的参数来使用的
    • 回调函数就是一个通过函数指针调用的函数
    • 回调函数是由别人的函数执行时调用你实现的函数

四、字符串

1、字符串的本质和定义

  •  字符串的本质就是字符数组,其定义的形式是这样的:char greeting[] = "Hello"

2、字符串常用函数(如下的函数都是在 c 的标准库<string.h>中的)

五、结构体(有点类似于 java 中的类的概念)

1、结构体的定义、初始化和访问

  •  数组是用来存储相同类型的数据的,而结构体是用来组织相关联的不同类型的数据的,其定义和初始化如下:
  • 结构体成员的访问方式和 java 中类成员的访问方式是类似的(book.title),需要注意的是c语言中字符串是不能直接赋值的,要使用strcpy函数来赋值(定义初始化的时候例外);
struct Books{   char  title[50];   char  author[50];   char  subject[100];   int   book_id;} book = {
"C 语言", "test", "编程语言", 123456};

2、结构体做函数参数和指向结构的指针

  • 结构体做函数的参数的使用和其他类型做函数参数没啥不同;
  • 指向结构体的指针使用:定义和通过指针访问结构体的成员
struct Books *book;  //define book->title   //access member

3、位域的概念

  • 位域的个人的理解就是对字节(byte)进行更加精确的位(bit)划分,利用划分出来的位段来存储数据,这样可以节省存储空间,并且便于处理;
  • 位域的定义如下,访问按照结构体成员的一般方式即可访问
struct bs{    int a:8;    int b:2;    int c:6;}data;
  • 关于位域的几点说明:
    • 位域在本质上就是一种结构类型,不过其成员是按二进位分配的;
    • 位域可以是无名位域,只用来作填充或调整位置,是不能使用的;
    • 关于位域的跨字节问题:很多教程说位域不能跨字节,但是实测是可以的,限制是不能超过原有类型的字节数(int 位域划分就不能超过 32 位);
    • 位域的赋值超过位数限定范围不会报错但是数值会出现重置的现象(4位的位域赋值 17,打印出来的值就是1);

4、用结构体实现类似于面向对象的编程方式

int max(int,int);struct ren{        int (*mymax)(int,int);    char name[50];} ren1 = {max,"haha"}; int main(void){    struct ren ren2;    strcpy(ren2.name,"ren2");    ren2.mymax = max;    printf("ren2 name = %s\n",ren2.name);    printf("ren2 max = %d\n",ren2.mymax(34,1000));    return 0;}int max(int a,int b){    return a>b?a:b;}

 

六、共用体

  • 共用体 是一种特殊的数据类型,允许在相同的内存位置存储不同的数据类型;
  • 可以定义一个带有多成员的共用体,但是任何时候只能有一个成员带有值,且占用的空间是按成员中占用空间最大的那个来算的;
  • 共用体变量同时使用会导致后面的覆盖前面的,使得前面的赋值出现损失,所以不要同时使用;
  • 共用体的定义和使用和结构体是差不多的:
union Data{   int i;   float f;   char  str[20];}; union Data haha; haha.i = 10;

 *******************************tips***********************************

  • typedef 可以用来为内置的数据类型和自定义的数据类型取别名,定义之后使用该类型可以直接用别名替代;
  • typedef 和 #define:
    • typedef 仅限于为类型定义符号名称,#define 不仅可以为类型定义别名,也能为数值定义别名;
    • typedef 是由编译器执行解释的,#define 语句是由预编译器进行处理的;

 

输入&输出

一、基本输入输出(需要标准库的支持<stdio.h>)

1、printf 和 scanf(格式化输出)

  • scanf("%s %d", str, i) 期待你的输入的类型和需要的类型相同,否则有可能报错;
  • scanf 遇到空格就会停止读取,所以 "this is test" 对 scanf 来说是三个字符串;

2、getchar 和 putchar(一个字符的输入和输出)

3、gets 和 puts(一行一行地读写,需要提供一个缓冲区)

char str[100];gets( str );puts( str );

二、文件读写

1、文件打开(fopen)

2、文件关闭(fclose)

3、文件写入(写字符:fputc  写字符串:fputs 和 fprintf)

4、文件读取(读字符:fgetc  读字符串:fgets 和 fscanf ----fgets 按行读取,而 fscanf 是以空格作为结束的)

5、二进制输入输出函数(fread 和 fwrite)

FILE * fp;fp = fopen ("file.txt", "w+");fprintf(fp, "%s %s %s %d", "We", "are", "in", 2014);fclose(fp);

预处理器和头文件

一、预处理器和宏

1、二者的概念

  • 预处理器就是一个文本替换的工具;
  • C 语言中的宏就是实现文本替换功能的C代码;

2、预处理器指令和运算符

  • 常见的预处理器指令如下:

  • 运算符:续行符(\)、字符串常量化运算符(#)、标记粘贴运算符(##)、defined() 运算符

3、预定义宏和参数化宏

  •  预定义宏常用的如下,不能直接修改:
    • __DATE__: 当前日期,一个以 "MMM DD YYYY" 格式表示的字符常量
    • __TIME__ :当前时间,一个以 "HH:MM:SS" 格式表示的字符常量
    • __FILE__ :这会包含当前文件名,一个字符串常量
  • 可以使用参数化的宏来模拟函数,必须要注意的是宏饿本质就是用来文本替换的,所以下面的实例中括号绝对不能少,少了可能出错:
int square(int x) {   return x * x;}可以用如下的来替换:#define square(x) ((x) * (x))

 

二、头文件(这里的用法类似于java中的import)

1、基本语法

  • #include <file>      引用系统头文件
  • #include  "file"       引用用户头文件

2、只引用一次

#ifndef HEADER_FILE#define HEADER_FILEint max(int x,int y){
return x;}#endif

3、条件引用

#if SYSTEM_1   # include "system_1.h"#elif SYSTEM_2   # include "system_2.h"#elif SYSTEM_3   ...#endif

 

 错误-递归-可变参-内存管理-命令行参数-排序算法

一、错误处理

1、C 语言没有提供类似 java 的错误处理的机制,只能在错误发生的时候输出一些错误的信息

2、错误信息的输出

  • 在发生错误时,大多数的 C 或 UNIX 函数调用返回 1 或 NULL,同时会设置一个错误代码 errno,该错误代码是全局变量,表示在函数调用期间发生了错误;
  • C 语言提供了 perror() 和 strerror() 函数来显示与 errno 相关的文本消息
#include 
#include
#include
extern int errno ;int main (){ FILE * pf; int errnum; pf = fopen ("unexist.txt", "rb"); if (pf == NULL) { errnum = errno; fprintf(stderr, "错误号: %d\n", errno); perror("通过 perror 输出错误"); fprintf(stderr, "打开文件错误: %s\n", strerror( errnum )); } else { fclose (pf); } return 0;}

3、程序的退出状态:程序正常退出exit(0);程序发生异常exit(-1);

二、递归

  • 1、所谓的递归就是函数自己调用自己,最经典的例子就是斐波那契数列的实现;
  • 2、递归一定要有退出条件,不然会形成死循环,导致程序出错;
  • 3、递归的次数不可过多,次数过多的话效率十分低下,还容易导致栈的溢出;
#include 
int fibonaci(int i){ if(i == 0) { return 0; } if(i == 1) { return 1; } return fibonaci(i-1) + fibonaci(i-2);} int main(){ int i; for (i = 0; i < 10; i++) { printf("%d\t\n", fibonaci(i)); } return 0;}

三、可变参(参数的数量不定)

1、可变参的使用要依赖于 C 的标准库:#include <stdarg.h>

  • 库变量:va_list(参数列表)
  • 库函数:va_arg(访问参数的某个项)、va_start(初始化valist)、va_end(清理valist内存)

2、详细使用示例

#include 
#include
double average(int num,...){ va_list valist; double sum = 0.0; int i; /* 为 num 个参数初始化 valist */ va_start(valist, num); /* 访问所有赋给 valist 的参数 */ for (i = 0; i < num; i++) { sum += va_arg(valist, int); } /* 清理为 valist 保留的内存 */ va_end(valist); return sum/num;} int main(){ printf("Average of 2, 3, 4, 5 = %f\n", average(4, 2,3,4,5)); printf("Average of 5, 10, 15 = %f\n", average(3, 5,10,15));}

 

四、内存管理

1、可变参的使用要依赖于 C 的标准库:#include <stdlib.h>

  • 动态分配内存:calloc(int num,int size)、malloc(int num)
  • 释放内存:free(void *address)
  • 重新分配内存:realloc(void *address, int newsize)

2、详细使用示例

#include 
#include
#include
int main(){ char name[100]; char *description; strcpy(name, "Zara Ali"); /* 动态分配内存 */ description = malloc( 30 * sizeof(char) ); //description = calloc(30,sizeof(char)); if( description == NULL ) { fprintf(stderr, "Error - unable to allocate required memory\n"); } else { strcpy( description, "Zara ali a DPS student."); } /* 假设您想要存储更大的描述信息 */ description = realloc( description, 100 * sizeof(char) ); if( description == NULL ) { fprintf(stderr, "Error - unable to allocate required memory\n"); } else { strcat( description, "She is in class 10th"); } printf("Name = %s\n", name ); printf("Description: %s\n", description ); /* 使用 free() 函数释放内存 */ free(description);}

 

五、命令行参数

  • int main( int argc, char *argv[] ):argc 是指传入参数的个数,argv[] 是一个指针数组,指向传递给程序的每个参数;
  • argv[0] 存储程序的名称,argv[1] 是一个指向第一个命令行参数的指针;
  • 多个命令行参数之间用空格分隔,如果参数本身带有空格,应把参数放置在双引号或单引号内部;

六、排序(几种常见排序的 C 代码练习)

七、标准库参考:

转载于:https://www.cnblogs.com/stanwuc/p/10766433.html

转载地址:https://blog.csdn.net/weixin_30896763/article/details/99352790 如侵犯您的版权,请留言回复原文章的地址,我们会给您删除此文章,给您带来不便请您谅解!

上一篇:Bootstrap基础10(标签页)
下一篇:SQL的四种连接(左外连接、右外连接、内连接、全连接)

发表评论

最新留言

不错!
[***.144.177.141]2024年04月08日 10时01分26秒