C

C 与 C++

静态类型,强类型(无泛型,但支持如 int 到 float 的隐式转换)

  • C 语言通常用于操作系统、软件、嵌入式开发(如Unix系统、Kindle、OpenGL 等)。
    C语言文件后缀为.c.h(头文件)。

  • C++ 是 C 的超集,与C语言最大的区别是支持面向对象编程。一个有效的C程序通常也是一个有效的C++程序。
    C++语言文件后缀为.cpp


编译

C/C++可以使用相同的编译器

CL

CL是微软的非开源编译器,通常包含在 Visual Studio 的环境包中

GCC

gcc 是 GNU 的开源编译器,其windows版本为minGW

  1. 下载(选择 x84_win32_seh版本,如在线安装失败也可以直接下载压缩包,但需要配置环境变量到mingw\mingw64\bin
  2. 输入gcc -v判断是否安装配置成功
  3. 通过gcc c语言文件名将文件编译为可执行文件
编译多个文件
  • 通过指令可以直接编译多个文件,如gcc a.c b.c -o c.exe
  • VSCode可以调用CL/GCC,本身没有编译功能
    配置完毕后工作目录下出现.vscode/task.json,修改后可变为多文件编译:
    "tasks": [
        {
            "type": "cppbuild",
            "label": "C/C++: gcc.exe 生成活动文件",
            "command": "D:\\C\\x86_mingw64\\bin\\gcc.exe",
            "args": [
                "-fdiagnostics-color=always",
                "-g",
                // "${file}",
                "${cwd}//*.c",
                "-o",
                "${fileDirname}\\${fileBasenameNoExtension}.exe"
            ],
      }
  ]

基础语法

#include  //预处理器指令,在实际编译前包含头文件

int main() //主函数
{
   /* 注释 */
   printf("Hello, W3Cschool! \n");//语句结束符;
   
   return 0;
}

注意多文件处于一个作用域中,因此全局函数/变量名不能重复,如main只能有一个。


输入和输出

%c 字符
%s 字符串
%d 10进制整数
%x 16进制整数
%f 10进制浮点数

int number;
scanf("%d", &number);
printf("number is %d \n", number);

数据类型

常见数据类型

char 整数类型,1字节(8位)
int 整数类型,4字节
float 单精度浮点数类型,4字节
double 双精度浮点数类型,8字节
void 无,如函数无返回值、函数无参数、指针指向void

  • sizeof()
    可以获得变量的字节数,如sizeof(100)返回4
  • signed 和 unsigned
    float、double总是带符号的,而整形中可手动定义是否带符号。
    char默认为无符号,其他整型默认为有符号。当需要变更时,如unsigned int = 10即可。
常量

字符常量是位于单引号''内的单个字符,也可以是如'\n'的特殊字符
字符串常量是位于双引号""内的一个或多个字符,下面这三种形式所显示的字符串是相同的:

"hello, dear"

"hello, \

dear"

"hello, " "d" "ear"
  • 定义常量
    常量必须同时完成声明、定义、赋值。
    使用#defineconst定义常量,通常定义为大写字母形式
#define LENGTH 10   
const int WIDTH = 5;
int area = LENGTH * WIDTH;
存储类

用于定义变量或函数的作用范围和生命周期

  • auto
    局部变量默认的存储类
{
   int mount;
   auto int month;
}
  • register
    向系统建议(实际是否可以由系统决定)存在寄存器(而非内存)的变量,因此变量最大尺寸为1字节。
    该变量没有内存地址,因此不可使用&操作符。
  • static
    修饰的局部变量不会在进入作用或离开时候创建或销毁
    修饰的全局变量/函数不能被其他源文件引用(外部链接属性变成了内部链接属性)
void fn()
{
    static int i = 0;
    i++;
    printf("%d""\n", i);
}
int main()
{
    fn();//1
    fn();//2
    fn();//3
}
  • extern
    声明,告诉编译器有这个变量存在,但不会为其分配空间。
    对于无法初始化的变量,会在同一作用域中找到另一个同名变量的存储位置(通常用于引用另一个文件中的变量或函数):
# a.c
#include 
int main()
{
    extern int a;
    printf("%d \n",a);
}
# b.c
#include 
int a = 200;

在python、js等弱类型语言中,不区分声明和定义,因其总是在赋值时才分配内存空间。但在C语言中,如 extern int a则只是声明,不分配内存空间;int a才是定义,分配了4字节空间(如此前无声明则同时做了声明和定义);int a = 1则是同时进行了声明、定义、赋值。一个变量可以多次声明,但只能定义一次。
此外,全局变量定义时会自动进行初始化。如int a;会初始化为0,char b;会初始化为'\0'


指针

指针是一种特殊的数据类型

  • 指针的值是目标对象的内存首地址(表现为一个整数,可以用%d%x打印出来)
  • 指针的类型是目标对象数据的类型

对指针的加减操作会指向另一个内存地址,因此每次整数的变化大小和该指针指向的数据类型字节数有关:

char value_1 = 'a';
char *test_1 = &value_1;
printf("test_1 from %d to %d \n", test_1, test_1 - 1); //test_1 from 6421823 to 6421822

double *test_2 = 1000;
test_2 = test_2 + 1;
printf("test_2 %d \n", test_2); // 1008
地址 &

返回变量地址,地址为一个int
注意,局部变量超出作用域后即销毁,因此无法在作用域外通过其地址获取值,除非定义局部变量为 static 变量

指针 *

用于定义一个指向另一个变量地址的指针,或将该指针解引用,还原为其值。
指针定义时所用的类型为其指向内容的类型。
未赋值的指针会初始化为一个随机值,通常手动赋值为NULL

int main()
{
    int a = 4;
    int *another_a = NULL;
    another_a = &a; /* 'another_a' 现在包含 'a' 的地址 */
    a = 222;
    printf("a 的值是 %d\n", a);//222
    printf("another_a 的值是 %x\n", another_a);//61fdf4
    printf("*another_a 是 %d\n", *another_a);//222
}

重复使用*则为指向指针的指针,同理,解引用时也需要使用多个*

int realInt = 9;
int *pointer_1 = &realInt;
int **pointer_2 = &pointer_1;
printf("printf realInt by pointer_2: %d", **pointer_2);//9
传值调用与引用调用

向函数传递参数时,默认为传值调用,修改函数内的形式参数不会影响实际参数。
通过为函数形参添加*,声明参数为引用类型,则改为引用调用

#include 

void swap(int *x, int *y);

int main ()
{
   /* 局部变量定义 */
   int a = 100;
   int b = 200;
   swap(&a, &b);

   printf("交换后,a 的值: %d\n", a );//200
   printf("交换后,b 的值: %d\n", b );//100
 
   return 0;
}

void swap(int *x, int *y)
{
   int temp;
   temp = *x;    /* 保存地址 x 的值 */
   *x = *y;      /* 把 y 赋值给 x */
   *y = temp;    /* 把 temp 赋值给 y */
  
   return;
}

数组

数组是一个固定大小的相同类型元素的顺序集合。
C中数组会占用连续的内存(类似C#的数组),因此。而JS的数组和Python的列表则是哈希映射。

  • 数据仅能在定义时赋值,如double balance[]={1000.0, 2.0, 3.4, 7.0, 50.0};
  • 若定义时未进行赋值,则必须声明数组的长度,如double balance[5];。未赋值的成员会根据数据类型自动初始化。
  • 其他场合下只能对数组中具体成员进行赋值,如balance[0]=2;
double arr2[] = {1.1, 2, 4, 8};
double *pp = arr2;
printf("%f \n", *(pp + 2));//4

数组变量是指向数组第一项的指针,因此可以用指针模拟一个数组。通过比较同一个数组中不同成员的指针地址大小,可以判断成员在数组中的先后关系:

const int MAX = 3;

int main ()
{
   int  var[] = {10, 100, 200};
   int  i, *ptr;
   ptr = var;
   i = 0;
   while ( ptr <= &var[MAX - 1] )
   {
      printf("Address of var[%d] = %x\n", i, ptr );
      printf("Value of var[%d] = %d\n", i, *ptr );
      ptr++;
      i++;
   }
   return 0;
}
多维数组
int a[3][4] = {  
 {0, 1, 2, 3} ,   /*  初始化索引号为 0 的行 */
 {4, 5, 6, 7} ,   /*  初始化索引号为 1 的行 */
 {8, 9, 10, 11}   /*  初始化索引号为 2 的行 */
};
指针数组

在数组名前加*则为指针数组:

int *pointerArray[2];
int int_1 = 1;
int int_2 = 2;
pointerArray[0] = &int_1;
pointerArray[1] = &int_2;
printf("value of pointerArray 1: %d \n",*pointerArray[0]);//1
printf("value of pointerArray 2: %d \n",*pointerArray[1]);//2
函数返回值

函数不能直接返回一个数组,但可以返回其指针(注意使用static,使局部变量可以在函数外被访问):

int *array()
{
    static int arr[3] = {1, 2, 3};
    return arr;
}
int main()
{
    int *p;
    p = array();
    printf("%d : %d \n", p + 0, *(p + 0));
    printf("%d : %d \n", p + 1, *(p + 1));
    printf("%d : %d \n", p + 2, *(p + 2));
}

字符串

C语言中,字符串是以'\0'结尾的字符数组(所以字符串实际占用的字节数比strlen获取的字符串长度多1)。
其中'\0'称为空字符或结束符(NUL,Null character),可以手工添加到字符数组尾,或者编译器会在初始化时自动添加。

char string[7] = {'R', 'U', 'N', 'O', 'O', 'B'};
printf("string: %s\n", string);//RUNOOB
char string2[] = "RUNOOB";
printf("string2: %s\n", string2);//RUNOOB
字符串数组

由于字符串本身是个字符数组,因此字符串数组就是字符的二维数组:

char stringstring[2][3] = {"aaa", "bbb"};
printf("stringstring %c \n", stringstring[1][1]);//b
printf("stringstring %s \n", stringstring[1]);//bbb

因为字符串是字符的数组,而数组的变量又是个指针,因此可以把字符串作为指针数组的成员,来表示字符串数组:

char *names[] = {
    "Zara Ali",
    "Hina Ali"
};
printf("%s \n", names[0] ); //Zara Ali

你可能感兴趣的:(C)