C语言基础

Hello, World!

介绍

C语言是一种通用编程语言,与机器工作密切相关。理解计算机内存如何工作是C语言的一个重要的层面。纵然C被认为是“难学的”,但实际上C是一种非常简单语言,有很大的可拓展性。

C是一种非常通用的语言,被用来编写很多应用,比如Windows、Python解释器、Git,等等。

C是一种编译型的语言,这意味着为了运行它,编译器就必须处理我们所写的的代码,然后建立一个可执行文件。这个文件可以被执行,然后完成我们想要做的任务。

我们的程序

每个C程序都使用库,以提供执行必要函数的可能。比如,最基础的函数叫做printf,(用来打印内容到屏幕),就是定义在stdio.h头文件中的。

为了能够运行printf命令,我们还必须直接地包含下面这行代码到我们的程序的第一行:

 #include <stdio.h>

程序的第二部分是我们实际要写的代码,main函数提供了运行的入口。

 int main() 
 {
     //... our code goes here
     printf("Hello, World!");
     return 0;
 }

int关键字指明了main函数将返回一个简单的整数。返回的数字将直接地指明我们的程序是否成功地工作。如果我们想说我们的程序成功地运行,我们将返回一个整数0,而大于0的整数意味着程序出现了某种错误。

注意:C语言程序的每一行都必须以分号(;)结束。

变量和类型

数据类型

C有很多类型的变量,以下是基础:

  • 整型 - 可正可负,使用char,int,short,long,long long来定义。
  • 非负整型 - 只能是正数,使用unsigned char,unsigned int,unsigned short,unsigned long,unsigned long long来定义。
  • 浮点型 - 实数,使用float,double来定义。
  • 结构体(后面会阐述),使用struct来定义。

不同类型的变量定义了它们的边界,char类型的范围从-128127,而long则从-2,147,483,6482,147,483,647,另一些类型在不同计算机上可能会有差别。

注意:C没有boolean类型。但是可以使用下面的方法定义:

 #define BOOL char
 #define FALSE 0
 #define TRUE 1

C使用字符数组来定义字符串,后面将会阐述。

定义变量

对于数字,我们通常使用类型int,在计算机里面占据一个word的大小(4 Bytes)。

定义变量foobar,需要使用下列语法:

 int foo;
 int bar = 1;

变量foo可以被使用,但我们没有初始化它,我们并不知道它实际上存储的是什么。变量bar包含了整数1

现在,我们可以做一些数学。假设abcde是变量,我们在下面使用加减乘除操作,以及将一个新的值给a

 int a = 0, b = 1, c = 2, d = 3, e = 4;
 a = b - c + d * e;
 printf("%d", a); /* will print 1-2+3*4 = 11 */

数组

数组是一种特殊的变量,可以包含超过一个相同类型的值,通过索引来访问。数组使用一种直接的语法来定义:

 /* defines an array of 10 integers */
 int numbers[10];

从数组中访问元素使用同样的语法。注意C中的数组是以0为第一个索引的,这意味着如果我们定义了 一个大小为10的数组,数组的下标是09numbers[10]不是一个实际的值。

 int numbers[10];
 
 /* populate the array */
 numbers[0] = 10;
 numbers[1] = 20;
 numbers[2] = 30;
 numbers[3] = 40;
 numbers[4] = 50;
 numbers[5] = 60;
 numbers[6] = 70;
 
 /* print the 7th number from the array, which has an index of 6 */
 printf("The 7th number in the array is %d", numbers[6]);

数组只能有一种类型的元素,因为它们在计算机内存是一系列连续存储的值。正是这样,访问数组中的每一个元素都非常的高效。

多维数组

在之前的数组章节中,我们概述了什么是数组及其工作原理。我们当时着眼的是一维数组,但C可以创建和使用多维数组,这里是声明多维数组的通用形式:

 type name[size1][size2]...[sizeN];

比如,这样:

 int foo[1][2][3];

或者,这样:

 char vowels[1][5] = {
     {'a', 'e', 'i', 'o', 'u'}
 };

二维数组

多维数组最简单的形式是二维数组。一个二维数组不过是拓展的一维数组而已,一维数组的的每一个元素都是一个列表:

 type arrayName[x][y];

type可以是C的任何数据类型(int,long,long long,double,etc),arrayName是一个合法的C标识符或者变量。二维数组也可以看成是xy列的表格,一个3行4列的数组可以表示如下:

二维数组

在这种情况下,数组中的每一个元素都可以用a[i][j]来表示,其中a是数组名,i,j是该元素在数组中的唯一下标。

其实,并不一定要指定数组a的行数,因为你可以像下面这样定义:

 char vowels[][5] = {
     {'A', 'E', 'I', 'O', 'U'},
     {'a', 'e', 'i', 'o', 'u'}
 };

编译器已经知道这是一个二维数组,你只需要指定有多少列!,编译器可能是智能的,它知道有多少个整数、字符、浮点数,无论你用多少维。

初始化二维数组

多维数组可以使用大括号({})来初始化每一行。下面是一个3行4列的数组。为了更加简化,你可以不写行数把它留空,编译照样通过。

 int a[3][4] = {  
    {0, 1, 2, 3} ,   /*  initializers for row indexed by 0 */
    {4, 5, 6, 7} ,   /*  initializers for row indexed by 1 */
    {8, 9, 10, 11}   /*  initializers for row indexed by 2 */
 };

内部的指定每一行的大括号是可选的。下面的初始化与之前的相同:

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

访问二维数组中的元素

二维数组中的元素是通过下标访问的,即,数组的行标和列标。

 int val = a[2][3];

上面的语句将会从数组中取出第3行第4列的元素。

条件

作出决策

生活中,我们常常需要做决定。为了作出决定我们权衡我们的选择,在程序中也是这样的。

这里是一个C语言的判断结构。

 int target = 10;
 if (target == 10) {
     printf("Target is equal to 10");
 }

if 语句

if语句允许我们检查表达式的真假(true or false),然后根据结果执行不同的代码。

使用==来判断两个变量是否相等,就像第一个例子那样。

不等运算符也可以用在判断表达式中。比如:

 int foo = 1;
 int bar = 2;
 
 if (foo < bar) {
     printf("foo is smaller than bar.");
 }
 
 if (foo > bar) {
     printf("foo is greater than bar.");
 }

当计算的判断表达式为false时,我们可以使用else关键字来执行我们的其他代码。

 int foo = 1;
 int bar = 2;
 
 if (foo < bar) {
     printf("foo is smaller than bar.");
 } else {
     printf("foo is greater than bar.");
 }

有时我们会得到超过一个选择,这时,使用多个if else来完成。

 int foo = 1;
 int bar = 2;
 
 if (foo < bar) {
     printf("foo is smaller than bar.");
 } else if (foo == bar) {
     printf("foo is equal to bar.");
 } else {
     printf("foo is greater than bar.");
 }

你也可以使用多个嵌套if else语句如果你喜欢。

 int peanuts_eaten = 22;
 int peanuts_in_jar = 100;
 int max_peanut_limit = 50;
 
 if (peanuts_in_jar > 80) {
     if (peanuts_eaten < max_peanut_limit) {
         printf("Take as many peanuts as you want!\n");
     }
 } else {
     if (peanuts_eaten > peanuts_in_jar) {
         printf("You can't have anymore peanuts!\n");
     }
     else {
         printf("Alright, just one more peanut.\n");
     }
 }

两个或者多个判断表达式也可以同时计算。判断两个表达式是否都是真(true),使用&,判断它们至少有一个为真,使用||

 int foo = 1;
 int bar = 2;
 int moo = 3;
 
 if (foo < bar && moo > bar) {
     printf("foo is smaller than bar AND moo is larger than bar.");
 }
 
 if (foo < bar || moo > bar) {
     printf("foo is smaller than bar OR moo is larger than bar.");
 }

非操作符可以用来取反:

 int target = 9;
 if (target != 10) {
     printf("Target is not equal to 10");
 }

字符串

定义字符串

C中的string实际上是字符数组。纵然使用指针是高级的话题,后面会阐述,我们将使用指针来定义简单的字符串,以下面的形式:

 char * name = "John Smith";

这种方法创建的string只能读。如果我们希望在字符串上操作,我们需要将它定义局部字符数组:

 char name[] = "John Smith";

这种表示法和上面的是有区别的,因为我们分配了一个数组变量所以我们能够操纵它。空的中括号[]告诉编译器来动态地计算数组的大小。

 char name[] = "John Smith";
 /* is the same as */
 char name[11] = "John Smith";

即使字符串John Smith正好只有11个字符,但我们还必须在定义的时候加上一个。这是由字符串的表示法决定的。C中字符串的结尾有一个特殊字符\0表示字符串的结束,但这个字符不会纳入字符串长度的计算中,只是便于编译器处理罢了。

使用printf来格式化字符串

我们使用printf命令来格式化一些字符串,以下面的形式:

 char * name = "John Smith";
 int age = 27;
 
 /* prints out 'John Smith is 27 years old.' */
 printf("%s is %d years old.\n", name, age);

注意:在打印字符串的时候,我们必须增加\n来换行,因为printf函数不是默认换行的。

字符串的长度

函数strlen传入字符串的地址,返回其长度。

 char * name = "Nikhil";
 printf("%d\n",strlen(name));

字符串比较

函数strncmp比较两个字符串,参数是两个字符串,以及要比较的长度;如果他们相等则返回0,若不同则返回其他数字。也有一种不太安全的版本叫strcmp,但不推荐使用,比如:

 char * name = "John";
 
 if (strncmp(name, "John", 4) == 0) {
     printf("Hello, John!\n");
 } else {
     printf("You are not John. Go away.\n");
 }

字符串拼接

函数strcat在后面增加nsrc中字符到dest中,其中,nmin(n,length(src));参数是源字符串src和目的字符串dest,以及要增加的最大字符的数量。

 char dest[20]="Hello";
 char src[20]="World";
 strncat(dest,src,3);
 printf("%s\n",dest);
 strncat(dest,src,20);
 printf("%s\n",dest);

For循环

C中的for循环是直接的。当一段代码需要多次执行时使用循环来解决,for循环使用一个迭代变量,通常表示为i

for循环给出如下定义:

  • 用初始值初始化迭代变量
  • 检查条件,判断是否已经到达最后的值
  • 改变迭代变量的值

比如说,我们希望迭代一段代码10次,我们会写:

 int i;
 for (i = 0; i < 10; i++) {
     printf("%d\n", i);
 }

注意:在for循环头部声明迭代变量是C++的语法(随处声明随处使用)。

这个代码快将打印数字09(一共10个数字)。

for循环也可以迭代数组的值。比如,我们想要计算数组内元素的总和,我们使用迭代变量i作为数组索引:

 int array[10] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
 int sum = 0;
 int i;
 
 for (i = 0; i < 10; i++) {
     sum += array[i];
 }
 
 /* sum now contains a[0] + a[1] + ... + a[9] */
 printf("Sum of the array is %d\n", sum);

While 循环

while循环与for循环很像,但功能更少。但条件为true时,while循环会不断地执行内部的代码块。比如说,下面的代码将会正好执行10次。

 int n = 0;
 while (n < 10) {
     n++;
 }

while循环也可以无限地执行,如果条件永远都是true的话。在C中,所有非0的值都是true

 while (1) {
    /* do something */
 }

循环的控制

有两种循环的控制方法,breakcontinue

break将结束整个循环,即使循环没有完全地执行指定的次数。

 int n = 0;
 while (1) {
     n++;
     if (n == 10) {
         break;
     }
 }

continue语句用来跳过当次循环,即,对于一次迭代,continue以下的语句将不会被执行。下面的例子,continue导致了某些情况下printf函数将被忽略,只有偶数才被输出。

 int n = 0;
 while (n < 10) {
     n++;
 
     /* check that n is odd */
     if (n % 2 == 1) {
         /* go back to the start of the while block */
         continue;
     }
 
     /* we reach this code only if n is even */
     printf("The number %d is even.\n", n);
 }

函数

C函数是简单的,但由于C的工作方式,函数的功能有一些限制。

  • 函数接收固定或者不定数量的参数
  • 函数只能返回一个值,或不返回任何值。

在C中,实际参数按值的方式拷贝,这意味着我们不能改变在函数外部的变量的值。如果要改变,我们必须使用指针,后面会讲到。

函数以下面的形式定义:

 int foo(int bar) {
     /* do something */
     return bar * 2;
 }
 
 int main() {
     foo(1);
 }

我们定义的函数foo接收一个整型参数bar,将其乘以2,然后返回。

调用函数foo采用以下语法,传入参数bar的值是1。

 foo(1);

在C中,函数必须先定义再使用。函数可以先在文件开始处或者头文件中声明,然后再实现;也可以按照使用的顺序实现。

The correct way to use functions is as follows:

使用函数的正确方法如下:

 /* function declaration */
 int foo(int bar);
 
 int main() {
     /* calling foo from main */
     printf("The value of foo is %d", foo(1));
 }
 
 int foo(int bar) {
     return bar + 1;
 }

我们也可以创建一个没有返回值的函数,使用关键字void

 void moo() {
     /* do something and don't return a value */
 }
 
 int main() {
     moo();
 }

Static

static是C语言关键字,可以用来修饰变量和函数。

什么是静态变量?

默认地,变量在定义的时候作用域都是局部的。将变量定义为static可以将其作用域提升到整个文件;结果,这些static变量可以在文件内部访问。

考虑下面的程序,我们想计算所参与的跑步者。

 #include<stdio.h>
 int runner() {
     int count = 0;
     count++;
     return count;
 }
 
 int main()
 {
     printf("%d ", runner());
     printf("%d ", runner());
     return 0;
 }

我们将会看到count并没有被更新,因为当函数完成的时候,变量count会从内存中移除。但是,当static变量使用时,情况将会不一样,整个文件使用同一个count,更改也会被任何函数发现。

 #include <stdio.h>
 int runner()
 {
     static int count = 0;
     count++;
     return count;
 }
 
 int main()
 {
     printf("%d ", runner());
     printf("%d ", runner());
     return 0;
 }

什么是静态函数?

在C中,函数默认都是全局的。如果我们将函数定义为static,它的作用域将降低到包含该函数的文件。

语法就像这样:

 static void fun(void) {
    printf("I am a static function.");
 }

静态 VS 全局

静态变量只在包含它的文件内部有效,而全局的变量可以在文件外部访问。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 205,386评论 6 479
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,939评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,851评论 0 341
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,953评论 1 278
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,971评论 5 369
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,784评论 1 283
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,126评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,765评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,148评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,744评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,858评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,479评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,080评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,053评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,278评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,245评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,590评论 2 343

推荐阅读更多精彩内容

  • 第一章 1.C语言学习中的重难点 运算符:自增、自减运算符(重点) 进制:各种进制转换、原码、反码、补码 数组:一...
    恒筠阅读 829评论 0 0
  • 这个是初始版本,还会进行改正和补充,请大家认真熟悉这些基础知识!如有错误,请在评论区留言@作者 总体上必须清楚的:...
    亦一银河阅读 2,492评论 3 40
  • C语言是当代人学习及生活中的必备基础知识,应用十分广泛,下面为大家带来C语言基础知识梳理总结,C语言零基础入门绝对...
    诸葛青云999阅读 12,084评论 0 2
  • 数据类型 基本数据类型 java基本数据类型 浮点型 字符型 boolean true/false C的基本数据类...
    醉了俗身醒了初心阅读 800评论 0 0
  • 平安长安创建工作专题宣传开展以来,区网信办按照区委主要领导的指示,在平安创建宣传工作中“注重代入感,增强参与性”的...
    指弹_09a4阅读 411评论 0 0