C语言编程

C简介

一、基本概念和特点

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

2、C语言的特点如下:

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

二、hello world

#include <stdio.h>

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 <stdio.h>
 
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 <stdio.h>
 
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_FILE
int 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 <stdio.h>
#include <errno.h>
#include <string.h>

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 <stdio.h>
 
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 <stdio.h>
#include <stdarg.h>
 
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 <stdio.h>
#include <stdlib.h>
#include <string.h>
 
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.runoob.com/cprogramming/c-standard-library.html 

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值