C 语言入门

1. C语言设计

1.1什么是C语言?

  • C语言是由Dennis Ritchie于1972年在美国AT&T的Bell实验室开发的一种编程语言。

  • 任何编程语言都可以分为两类。

    • 面向问题(高级语言)
    • 面向机器(低级语言)

    但是C被认为是Middle level Language

  • C是modularportablereusable

1.2 C语言的特点

  • 结构化语言
    • 它具有划分和隐藏所有信息和指令的能力。
    • 可以使用函数或代码块在C中对代码进行分区。
    • 与其他语言相比,C是一种结构良好的语言。
  • 通用语言
    • 使之成为系统编程的理想语言。
    • 它也可以用于商业和科学应用。
    • ANSI于1983年建立了c标准。
    • c的功能是操纵位,字节和地址。
    • 1990年后期采用。
  • 可移植性
    • 可移植性是移植或使用所编写软件的能力。
    • 一个计算机C程序可以重复使用。
    • 通过修改或不修改。
  • 代码可重用性和自定义和扩展能力
    • 程序员可以轻松创建自己的函数
    • 可以在不同的应用中重复使用
    • C程序基本功能集合
    • 该功能由“ c”库支持
    • 函数可以连续添加到“ c”库中
  • 关键字数量有限
    • “ C”中只有32个关键字
    • Ritchie提供了27个关键字
    • ANSI添加5
    • “ C”的优势在于其内置功能
    • Unix系统提供大量的C函数
    • 操作中使用了某些功能。
    • 其他的则专门用于其应用

1.3 C程序结构

pre-processor directives
global declarations

main()
{
    local variable deceleration
    statement sequences
    function invoking
}

1.4 C关键字

关键字是已经向C编译器解释其含义的单词。C中只有32个可用关键字。这些关键字也称为“保留字”。

auto        double      int         struct 
break       else        long        switch 
case        enum        register    typedef 
char        extern      return      union 
const       float       short       unsigned 
continue    for         signed      void 
default     goto        sizeof      volatile 
do          if          static      while

1.5 C字符集

字符表示用于表示信息的任何字母,数字或特殊符号。以下是C中允许的有效字母,数字和特殊符号。

  • Alphabets -A,B,……,Y,Z a,b,……,y,z
  • Digits - 0, 1, 2, 3, 4, 5, 6, 7, 8, 9
  • Special symbols - 〜 ’ !@#%^&*( )_ - + = | \ { } [ ]:; ’ " < >,。?/

1.6编写,编译和执行C程序的规则

  • C是区分大小写的,表示变量“ COUNTER”与变量“ counter”不同。
  • 所有关键字均小写。
  • 关键字不能用于任何其他目的(如变量名)。
  • 每个C语句必须以;。结尾。因此;充当语句终止符。
  • 第一个字符必须是字母或下划线,下划线以外的任何特殊符号,变量,常量或关键字中不允许使用逗号或空格。
  • 可以在两个单词之间插入空格以提高语句的可读性。但是,在变量,常量或关键字中不允许有空格。
  • 必须先声明变量,然后才能在程序中使用它。
  • 文件应具有扩展名.c
  • 程序需要在执行之前进行编译。

1.7数据类型和占位符

  • C具有5种基本的内置数据类型。
  • 数据类型定义了一组变量可以存储的值以及可以对其执行的一组操作。
  • 变量在不同的时间采用不同的值。
  • 声明变量的一般形式是:
    类型名称;
  • 下面是使用变量的示例:
#include<stdio.h> 
main() 
{ 
    int sum; 
    sum=12; 
    sum=sum+5; 
    printf("Sum is %d",sum); 
}

printf函数将打印以下内容:
总和为17
实际上%d是整数变量值的占位符,其名称位于双引号后。

  • 常见的数据类型有:
    • int - 整数
    • char -人物
    • long -长整数
    • float -浮点数
    • double -长浮
  • 其他占位符是:
Placeholders        Format
%c                  Character
%d                  Signed decimal integer
%i                  Signed decimal integer
%e                  Scientific notation[e]
%E                  Scientific notation[E]
%f                  Decimal floating point
%o                  unsigned octal
%s                  String of character
%u                  unsigned decimal integer
%x                  unsigned Hexadecimal (lower)
%X                  unsigned Hexadecimal (upper)
%p                  dispaly a pointer
%%                  print a %

1.8控制字符(转义序列)

某些非打印字符以及反斜杠()和撇号(’)可以用转义序列表示。

  • \a -贝尔
  • \n - 新队
  • \r -回车
  • \b -退格键
  • \f -换页
  • \t -水平标签
  • \" -引号
  • \v -垂直标签
  • \' -使徒
  • \\ -反斜杠
  • \? -问号
  • \0 - 空值

1.9从键盘接收输入值

scanf用于从键盘接收输入的功能。
scanf函数的一般形式是:

scanf("Format string",&variable,&variable,...); 

Format string包含我们打算从键盘接收的变量的占位符。&变量列表中的每个变量名称前都有一个符号。字符串是该规则的例外。他们不会在他们面前带有这个标志。

注意:除占位符和某些特殊字符外,不允许您在格式字符串中插入任何其他字符。甚至输入空格或其他不希望的字符都将导致您的程序无法正常工作,并且结果将是意外的。因此,请确保仅以scanf格式字符串插入占位符。以下示例从键盘接收多个变量。

float a; 
int n; 
scanf("%d%f",&n,&a);

请注意,scanf函数没有内置的错误检查功能。程序员负责验证输入数据(类型,范围等)并防止错误



2.表达式和运算符优先级

下表总结了所有运算符的优先级和关联性规则,包括我们尚未讨论的规则。同一行上的运算符具有相同的优先级;行按优先级从高到低的顺序排列,例如,*,/和%都具有相同的优先级,该优先级高于二进制+和-。``运算符’’()表示函数调用。运算符->和。用于访问结构体的成员;

描述操作员社交性
函数表达式()左到右
数组表达式[]左到右
结构体算子->左到右
结构体算子.左到右
一元减-右到左
增/减++, –右到左
称赞~右到左
否定!右到左
地址右到左
地址值*右到左
类型转换(类型)右到左
大小(以字节为单位)大小右到左
乘法*左到右
/左到右
模量%左到右
加成+左到右
减法-左到右
左移<<左到右
右移>>左到右
少于<左到右
小于或等于<=左到右
比…更棒>左到右
大于或等于> =左到右
等于==左到右
不等于!=左到右
按位与左到右
按位异或^左到右
按位或|左到右
逻辑与&&左到右
逻辑或||左到右
有条件的?:右到左
分配=,* =,/ =,%=,+ =,-=,&=,^ =,| =,<< =,>> =右到左
,右到左

一元&+,-和*的优先级高于二进制形式。



3.决策控制结构

C具有三个主要的决策指令-if语句,if-else语句和switch语句。

3.1 if语句

C使用关键字if来执行决策控制指令。if语句的一般形式如下:

//for single statement
if(condition) 
    statement;
  
//for multiple statement
if(condition) 
{ 
    block of statement; 
}

更一般的形式如下:

//for single statement
if(expression) 
    statement;
  
//for multiple statement
if(expression) 
{ 
    block of statement; 
}

这里的表达式可以是任何有效的表达式,包括关系表达式。我们甚至可以在if语句中使用算术表达式。例如,以下所有if语句均有效

if (3 + 2 % 5) 
    printf("This works");

该表达式的(3 + 2 % 5)计算结果为5,并且由于5为非零值,因此被认为是正确的。因此,printf("This works");执行。

3.2 if-else语句

如果if之后的表达式为true,则if语句本身将执行一个语句或一组语句。表达式的计算结果为false时,它将不执行任何操作。如果表达式的计算结果为true,我们可以执行一组语句,如果表达式的计算结果为false,我们可以执行另一组语句吗?当然!这就是else语句被证明为的目的

if (expression)
{ 
    block of statement;
}
else
    statement;

注意

  • if直到之后但不包括的else一组语句称为if block。同样,else表格后的语句也为else block
  • 请注意,else恰好写在的下方if。在该声明if block和那些在else block已经向右缩进。
  • 如果只有一个语句要在中执行,if block而我们中只有一个语句else block可以删除这对花括号。
  • 与该if语句一样,的默认范围else也是紧接在后面的语句else。要覆盖此默认范围,必须使用上述*“ if中的多个语句”*所示的一对大括号。

3.3嵌套if-els

如果我们if-elseif语句主体或语句主体中编写整个构造else。这就是所谓的*“嵌套”*的ifs。这表现为-

if (expression1)
    statement;
else 
{
    if (expression2)
        statement;  
    else
    { 
        block of statement;
    }
}

3.4 if-else梯子/ else-if子句

else-if是编写多向决策的最通用方法。

if(expression1) 
    statement; 
else if(expression2) 
    statement; 
else if(expression3) 
    statement; 
else if(expression4) 
{ 
    block of statement; 
} 
else 
    statement;

表达式在顺序评估; 如果表达式为真,则执行与其关联的*“语句”“语句块”,这将终止整个链。与往常一样,每个语句的代码可以是单个语句,也可以是一组用大括号括起来的代码。最后else一部分处理“以上所有内容”或其他情况都不满足的默认情况*。

3.5切换语句或控制语句

switch语句是一个多向决策,它测试表达式是否与多个constant整数值之一匹配,并相应地分支。switch允许我们根据选择数量做出决定的语句称为a switch,或更正确地说是a switch-case-default,因为这三个关键字共同构成了switch语句。

switch (expression) 
{
    case constant-expression: 
        statement1;
        statement2;
        break;
    case constant-expression: 
        statement;
        break;
    ...
    default: 
        statement;
}
  • switch…case命令中,每个命令都case像一个简单的标签。标签确定程序中必须从此处继续执行的点。Switch语句case将从继续执行程序的部分或标签中选择一个。程序将继续执行,直到到达break命令为止。
  • break陈述在switch结构上具有至关重要的规则。如果删除这些语句,程序将继续执行下一个case部分和所有剩余的case部分,直到switch执行块的末尾为止(大多数时候,我们只希望case运行一个部分)。
  • default 如果所有案例部分都不匹配开关比较,则将执行该部分。

3.6 switch vs if-else

有些情况无法使用switch
如:

  • 浮点表达式不能使用switch
  • case永远不能有变量表达式(例如 ,case a +3: 是错误的)
  • 多个case不能使用相同的表达式。


4.循环控制结构

有时,我们希望代码的某些部分被多次执行。我们可以在程序中重复代码,也可以使用循环。显然,例如,如果我们需要执行一部分代码一百次,则重复代码是不现实的。或者,我们可以在循环内使用我们的重复代码。

有三种方法forwhile并且do-while我们可以重复的程序的一部分。

4.1 while循环

while循环由条件或表达式以及必须
在循环中运行的单个命令或命令块构成。

//for single statement  
while(expression) 
    statement;

//for multiple statement   
while(expression) 
{ 
    block of statement 
} 

while循环中的语句将继续执行,直到要测试的条件保持为真为止。当条件变为假时,控制传递到while循环主体之后的第一条语句。

的一般形式while如下所示:

initialise loop counter;  
while (test loopcounter using a condition) 
{ 
    do this; 
    and this; 
    increment loopcounter;
} 

4.2 for循环

for循环类似于while循环,但更为复杂。for循环是由一个控制语句(该语句确定循环将运行多少次)和一个命令部分构成的。命令部分是单个命令或命令块。

//for single statement  
for(control statement) 
    statement;  

//for multiple statement
for(control statement) 
{ 
    block of statement 
} 

控制语句本身包含三个部分:

for ( initialization; test condition; run every time command )
  • Initialization``for循环启动时,零件仅执行一次。我们可以在这里初始化循环变量。
  • Test condition是循环中最重要的部分。如果此条件有效(true),循环将继续运行。如果条件变为无效(假),则循环将终止。
  • Run every time command该部分将在每个循环周期中执行。我们使用这部分来达到终止循环的最终条件。**例如,**我们可以通过以下方式增加或减少循环变量的值:指定的循环次数后,循环条件变为无效,并且for循环可以终止。

4.3 do-while循环

whilefor循环测试顶部的终止条件。相比之下,C中的第三个循环()在do-while每次通过循环体后在底部进行测试;身体总是至少执行一次。
做的语法是

do
{
    statements;
}while (expression);

该语句被执行,然后expression被评估。如果为true,则再次评估语句,依此类推。当表达式为假时,循环终止。经验表明,do-while这种用法要比while和少得多for。使用do-while循环可确保循环内的语句至少执行一次。

4.4 Break and Continue语句

我们以前breakswitch...case结构中使用过语句。我们还可以
在循环内部使用“ break”语句终止循环并退出循环(具有特定条件)。

在以上示例中,循环执行继续进行,直到num>=20输入的分数或为负数为止。

while (num<20) 
{ 
    printf("Enter score : "); 
    scanf("%d",&scores[num]); 
    if(scores[num]<0) 
        break; 
} 

Continue语句可以在循环中使用。像breakcommand一样continue更改程序流程。但是,它不会终止循环。它只是跳过当前循环的其余部分,并返回到循环的起点。

#include<stdio.h> 
main() 
{ 
    while((ch=getchar())!='\n') 
    { 
        if(ch=='.') 
            continue; 
        putchar(ch); 
    }  
} 

在上面的示例中,程序接受所有输入,但省略了“。” 从它的字符。输入文本时将回显该文本,但是在按回车键(等于插入“ \ n”字符)后,将输出主输出。如前所述,这是因为getchar()函数是缓冲输入函数。

4.5 跳转goto和标签

C提供了无限滥用的goto语句,并labels跳转到该语句。正式地,goto语句从没有必要,实际上,没有它,编写代码几乎总是容易的。本书未使用goto。

但是,在某些情况下,gotos可能会找到位置。最常见的是放弃某些深层嵌套结构的处理,例如一次脱离两个或多个循环。break语句只能从最内部的循环中退出,因此不能直接使用。从而:

for ( ... )
{           
    for ( ... ) 
    {               
        ...               
        if (disaster)
        {                   
            goto error;
        }           
    }
}       
...   
error:       
/* clean up the mess */

如果错误处理代码很简单,并且错误可能在多个地方发生,则此组织非常方便。

Alabel的形式与变量名相同,后跟冒号。它可以附加到与goto相同功能的任何语句中。标签的范围是整个功能。

-通过使用goto,程序变得不可靠,不可读且难以调试。然而,许多程序员发现诱人的。



5.数组

数组是保存相同数据类型的多个变量的结构。数组中的第一个元素编号为0,因此最后一个元素比数组的大小小1。数组也称为下标变量。在使用数组之前,必须声明其类型和维数。

5.1数组声明

像其他变量一样,需要声明一个数组,以便编译器知道哪种数组以及所需的数组大小。

int marks[30] ;

在这里,int指定变量的类型,就像普通变量一样,单词marks指定变量的名称。该[30]然是新的。数字30表示类型int将在我们的数组中有多少个元素。此数字通常称为数组的*“维数”*。括号( [ ] )告诉编译器我们正在处理数组。

现在让我们看看如何在声明数组时初始化它。以下是一些证明这一点的例子。

int num[6] = { 2, 4, 12, 5, 45, 5 } ; 
int n[] = { 2, 4, 12, 5, 45, 5 } ; 
float press[] = { 12.3, 34.2 -23.4, -11.3 } ;

5.2访问数组的元素

声明数组后,让我们看看如何引用数组中的各个元素。这是通过下标完成的,下标是数组名称后面方括号中的数字。此数字指定元素在数组中的位置。所有数组元素都从0开始编号。因此,标记[2]不是数组的第二个元素,而是第三个元素。

int valueOfThirdElement = marks[2];

5.3将数据输入数组

这是将数据放入数组的代码部分:

for(i = 0;i <= 29;i++) 
{ 
    printf("\nEnter marks "); 
    scanf("%d", &marks[i]); 
}

for循环导致向用户请求和接收学生成绩的过程重复30次。第一次通过循环,i其值为0,因此该scanf()函数将导致键入的值存储在数组元素marks[0](数组的第一个元素)中。重复此过程直到i29。这是最后一次循环,这是一件好事,因为没有像这样的数组元素marks[30]

scanf()函数中,我们在数组的元素上使用了*“地址”*运算符(&)marks[i]。这样,我们将这个特定数组元素的地址传递给scanf()函数,而不是传递给它的值。这是scanf()需要的。

5.4从阵列读取数据

程序的剩余部分将从数组中读取数据,并使用它来计算平均值。for循环非常相似,但是现在循环的主体使每个学生的分数加到存储在称为sum的变量中的运行总计中。当所有分数相加后,结果除以学生人数30,得到平均值。

for ( i = 0 ; i <= 29 ; i++ )
    sum = sum + marks[i] ; 
avg = sum / 30 ; 
printf ( "\nAverage marks = %d", avg ) ;

5.5范例

让我们尝试编写一个程序,以查找由
30名学生在一次测试中获得的平均成绩。

#include<stdio.h>  
main() 
{ 
    int avg, i, sum=0; 
    int marks[30] ; /*array declaration */ 
    for ( i = 0 ; i <= 29 ; i++ ) 
    { 
        printf ( "\nEnter marks " ) ; 
        scanf ( "%d", &marks[i] ) ; /* store data in array */ 
    } 
    for ( i = 0 ; i <= 29 ; i++ ) 
        sum = sum + marks[i] ; /* read data from an array*/ 
    avg = sum / 30 ; 
    printf ( "\nAverage marks = %d", avg ) ; 
} 


6字符串

6.1什么是字符串?

字符串是字符数组。数组的每个成员都包含字符串中的一个字符。

#include<stdio.h> 
main() 
{ 
    char name[20]; 
    printf("Enter your name : "); 
    scanf("%s",name); 
    printf("Hello, %s , how are you ?\n",name); 
} 

输出结果:

Enter your name : Vineet 
Hello, Vineet, how are you ?

如果用户输入“ Vineet”,则数组的第一个成员将包含“ V”,第二个单元格将包含“ i”,依此类推。C通过零值字符确定字符串的结尾。我们称这个字符为NULL字符,并用\0字符显示它。(它只是一个字符,其值为0,但是我们将其显示为两个字符以记住它是字符类型,而不是整数)。

同样,我们可以通过为每个成员分配字符值来创建该字符串。

name[0]='B'; 
name[1]='r'; 
name[2]='i'; 
name[3]='a'; 
name[4]='n'; 
name[5]='\0';

如上例所示,字符串变量的占位符为%s。同样,我们不会使用&符号来接收字符串值。

6.2注意事项

使用输入字符串时,scanf()我们必须注意
两点:

  • 字符串的长度不应超过字符数组的尺寸。这是因为C编译器不对字符数组执行边界检查。
  • scanf()无法接收多字字符串。因此,诸如“ Vineet Choudhary”的名称将是不可接受的。解决此限制的方法是使用函数gets()。函数的用法gets()及其对应部分puts()如下所示。
#include<stdio.h> 
main( ) 
{ 
    char name[25] ; 
    printf ("Enter your full name ") ; 
    gets (name) ; 
    puts ("Hello!") ; 
    puts (name) ; 
} 

这是输出…

Enter your name Vineet Choudhary 
Hello! 
Vineet Choudhary

该程序和输出是不言自明的,除了以下事实:一次puts()只能显示一个字符串(因此在puts()上面的程序中使用了两个)。另外,在显示字符串时,与不同printf()puts()将光标置于下一行。尽管gets()一次只能接收一个字符串,但加点gets()是它可以接收一个多单词的字符串。

如果我们准备好解决麻烦,可以scanf()通过以下方式编写它来接受多字字符串:

char name[25] ; 
printf ("Enter your full name ") ; 
scanf ("%[^\n]s", name) ;

尽管可行,但这是调用函数的最佳方式,但您会同意。

6.3标准库字符串函数

对于每个C编译器,string.h文件中都提供了大量有用的字符串处理库函数。

  • strlen -查找字符串的长度
  • strlwr -将字符串转换为小写
  • strupr -将字符串转换为大写
  • strcat -在另一个字符串的末尾附加一个字符串
  • strncat-在
    另一个字符串的末尾追加字符串的前n个字符
  • strcpy -将字符串复制到另一个
  • strncpy -将一个字符串的前n个字符复制到另一个
  • strcmp -比较两个字符串
  • strncmp -比较两个字符串的前n个字符
  • strcmpi-比较两个字符串而不考虑大小写(“ i”表示
    此函数忽略大小写)
  • stricmp-比较两个字符串而不考虑大小写(与
    strcmpi相同)
  • strnicmp-比较两个字符串的前n个字符,而不考虑
    大小写
  • strdup -复制字符串
  • strchr -查找字符串中给定字符的首次出现
  • strrchr -查找字符串中给定字符的最后一次出现
  • strstr -查找给定字符串在另一个字符串中的首次出现
  • strset -将字符串的所有字符设置为给定字符
  • strnset -将字符串的前n个字符设置为给定字符
  • strrev -反转字符串


7.函数

7.1什么是函数?

一个函数由一个代码块组合而成,可以通过调用名称在程序中的任何位置调用或使用该代码块。函数的主体以开头,以{结束}。这类似于主要功能。下面的示例显示了我们如何编写一个简单的函数。

#include<stdio.h> 

/*Function prototypes*/ 
myfunc();
 
main() 
{ 
    myfunc();  
}

/*Function Defination*/ 
myfunc() 
{ 
    printf("Hello, this is a test\n"); 
} 

在上面的示例中,我们将程序的一部分放在了单独的函数中。但是功能主体可能非常复杂。创建函数后,我们可以使用其名称来调用它。函数也可以互相调用。函数甚至可以调用自身。

顺便注意function prototypes一节。在某些C编译器中,我们需要在程序之上介绍我们正在程序中创建的函数。引入了一个函数function prototype

注意事项

  • 任何C程序都至少包含一个函数。
  • 如果程序仅包含一个函数,则它必须是main()。
  • 如果C程序包含多个函数,则这些功能中的一个(只有一个)必须是main(),因为程序执行总是从main()开始。
  • C程序中可能存在的函数数量没有限制。
  • 程序中的每个函数都按照main()中的函数调用指定的顺序进行调用。
  • 每个函数完成其操作后,控制权返回到main()。当main()用完函数调用时,程序结束。

7.2为什么使用函数

为什么要编写单独的函数?为什么不将整个逻辑压缩为一个函数main()?
两个原因:

  • 编写函数可以避免一遍又一遍地重写相同的代码。
  • 使用函数可以更轻松地编写程序并跟踪它们在做什么。如果程序的操作可以划分为单独的活动,并且每个活动都放置在不同的功能中,那么每个活动都可以或多或少地独立编写和检查。将代码分成模块化功能也使程序更易于设计和理解。

**这个故事的寓意是什么?**不要试图在一个函数中塞满整个逻辑。这是一种非常糟糕的编程风格。取而代之的是,将程序分成小部分,并为每个孤立的细分编写函数。不要犹豫编写仅调用一次的函数。重要的是这些功能执行一些逻辑上隔离的任务。

7.3函数参数

函数能够接受变量形式的输入参数。这些输入参数变量
然后可以在函数主体中使用。

#include<stdio.h> 
/* use function prototypes */ 
sayhello(int count);
main()
{ 
    sayhello(4);  
} 

sayhello(int count) 
{ 
    int c; 
    for(c=0;c<count;c++) 
        printf("Hello\n"); 
} 

在上面的示例中,我们sayhello()使用参数调用了function 4。该函数接收输入值,并count在开始执行函数主体之前将其分配给变量。sayhello()然后功能将count在屏幕上显示问候消息时间。

7.4函数返回值

在数学中,我们通常期望函数返回一个值。它可以接受也可以不接受参数,但始终返回一个值。

return值都有一个类型为C.它可以是其他的值intfloatchar或其他任何东西。此return值的类型确定您的函数的类型。

函数的默认类型是int或整数。如果您未指定要使用的功能类型,则该类型为int。如前所述,每个函数必须返回一个值。我们使用return命令执行此操作。

int sum() 
{ 
    int a,b,c; 
    a=1; 
    b=4; 
    c=a+b; 
    reurn c; 
} 

上面的函数返回变量c的值作为函数的返回值。我们还可以在return命令中使用表达式。**例如,**我们可以将函数的最后两行替换为return a+b;如果您忘记在函数中返回值,则在大多数C编译器中都会收到警告消息。此消息将警告您函数必须返回一个值。警告不会停止程序执行,但会因错误而停止执行。

在前面的示例中,我们未在函数中返回任何值。例如,您必须在main()函数中返回一个值。

main() 
{ 
    . 
    . 
    . 
    return 0; 
} 

int类型函数的默认返回值为0。如果未return 0main()函数中插入或其他任何值,则将自动返回0值。如果打算int在函数中返回值,则最好在函数标头和make中提及返回值。

7.5无效返回值

voidC语言还有另一种功能。无效类型函数是没有return值的函数。您可以将不需要返回值的函数定义为void

void test () 
{ 
    /* fuction code comes here but no return value */ 
}

void函数不能分配给变量,因为它不返回值。所以你不能写:

a=test(); 

使用以上命令将产生错误。

7.6递归函数

在C语言中,函数可以自行调用。如果函数recursive主体中的语句调用同一函数,则会调用该函数。

以下是计算阶乘值的递归函数。

#include<stdio.h>
int rec(int);
main() 
{ 
    int a, fact ; 
    printf("\nEnter any number "); 
    scanf ("%d", &a); 
    fact = rec(a); 
    printf ("Factorial value = %d", fact); 
} 
int rec (int x) 
{ 
    int f; 
    if (x == 1) 
        return (1); 
    else 
        f = x * rec (x - 1) ; 
    return (f) ; 
}

输出-

Enter any number 5 
Factorial value = 120

让我们了解此递归阶乘函数。

发生的是

rec(5) returns(5 * rec(4), 
 which returns (4 * rec(3), 
  which returns (3 * rec(2), 
   which returns (2 * rec(1), 
    which returns (1)))))

狐狸?好吧,这是最简单的服装对您的递归。我希望您同意,很难直观地看到控件如何从一个函数调用流向另一个函数。可能的以下数字会使事情变得更清晰。
recursive

7.7多个参数的函数

实际上,您可以在一个函数中使用多个参数。以下示例将向您展示如何执行此操作。

#include<stdio.h> 
int min(int a,int b); 
main() 
{ 
    int m; 
    m=min(3,6); 
    printf("Minimum is %d",m);  
    return 0; 
} 
int min(int a,int b) 
{ 
    if(a<b) 
        return a; 
    else 
        return b; 
} 

如您所见,您可以轻松地将变量添加到参数列表。

7.8按值调用

C编程语言函数调用,使用按值调用方法。让我们看一个例子,以更好地理解该主题。

#include<stdio.h> 
void test(int a); 
main() 
{ 
    int m; 
    m=2; 
    printf("\nM is %d",m); 
    test(m); 
    printf("\nM is %d\n",m); 
    return 0; 
} 
void test(int a) 
{ 
    a=5; 
} 

main()函数中,我们声明了一个变量m。我们已将值分配2m。我们想看看函数调用是否能够更改值,m因为我们已经修改了函数内部传入参数的值test()

程序输出结果为:

M is 2 
M is 2

因此,您可以看到函数调用未更改参数的值。这s是因为函数调用方法仅将变量的值发送m到函数,而不发送变量本身。实际上,它将变量的值m放在称为堆栈的存储位置中,然后函数无需访问主变量本身即可检索该值。这就是为什么我们将这种称为*“按值调用”的*方法称为原因。

7.9按引用调用

发送变量的另一种方法称为*“按引用调用”*。第二种方法使函数能够修改函数调用中使用的参数变量的值。

我们将首先看到一个示例,然后我们将对其进行描述。

#include<stdio.h> 
void test(int *ptr); 
main() 
{ 
    int m; 
    m=2; 
    printf("\nM is %d",m); 
    test(&m); 
    printf("\nM is %d\n",m); 
    return 0; 
} 
void test(int *ptr) 
{ 
    printf("\nModifying the value inside memory address%ld",&m); 
    *ptr=5; 
} 

为了能够修改用作函数中参数的变量的值,函数需要知道变量的内存地址(变量在内存中的确切位置)。

在C编程语言中,&运算符给出存储变量的地址。例如,如果m是类型的变量int,然后&m将赋予我们variable.We的起始内存地址调用这个结果地址a pointer

ptr=&m;

在上述命令中,ptr变量将包含变量的内存地址m。此方法在C语言的某些标准函数中使用。例如, scanf function使用此方法能够从控制台键盘接收值并将其放在变量中。实际上,它将接收到的值放在函数中使用的变量的存储位置中。现在我们了解了为什么&在变量中的变量名称之前添加符号的原因scanf

scanf(“%d”,&a);

现在我们有了变量的内存地址,我们必须使用一个运算符,该运算符使我们能够分配一个值或访问该地址中存储的值。

如前所述,我们可以a使用&运算符找到变量的地址。

ptr=&a; 

现在我们可以a使用*运算符找到存储在变量中的值:

val=*ptr; /* finding the value ptr points to */

我们甚至可以修改地址内的值:

*ptr=5; 

让我们再次来看我们的例子。我们已经将指针(内存地址)传递给了函数。现在函数可以修改存储在变量中的值。如果您是该程序,则将获得以下结果:

M is 2 
Modifying the value inside memory address 2293620
M is 5

因此,您看到这一次,我们已更改了被调用函数内的参数变量的值(通过修改主变量的内存位置内的值)。



8.指针

8.1什么是指针?

指针是一个包含变量地址的变量。最主要的是,一旦您可以讨论变量的地址,便可以转到该地址并检索存储在其中的数据。

8.2 C指针语法

指针需要一些新的语法,因为当您拥有一个指针时,您既需要请求其存储的存储位置,又需要存储在该存储位置的值的能力。此外,由于指针有些特殊,因此当您声明指针变量该变量是指针时,您需要告诉编译器,并告诉编译器它指向什么类型的内存。

指针声明如下所示:

<variable_type> *<name>;

例如,您可以使用以下语法声明一个指针,该指针存储整数的地址:

int *points_to_integer;

注意*的使用。这是声明指针的关键。如果将其直接添加到变量名之前,它将声明该变量为指针。

8.3指针符号

考虑一下声明,

int i = 3; 

该声明告诉C编译器:

  • 在内存中保留空间以容纳整数值。
  • 将名称i与此存储位置相关联。
  • 将值3存储在此位置。

我们可以i’s通过以下内存映射表来表示内存中的位置。

VariableMemory

我们看到计算机已选择内存位置65524作为存储值3的位置。位置号65524不是要依赖的数字,因为其他时间计算机可能会选择其他位置来存储值3。重要的一点是,我在内存中的地址是一个数字。我们可以通过以下程序打印该地址号码:

#include<stdio.h>
main() 
{ 
    int i = 3 ; 
    printf("\nAddress of i = %u",&i); 
    printf("\nValue of i = %d",i); 
} 

上面程序的输出为:

Address of i = 65524 
Value of i = 3

printf()仔细看第一句话。&此语句中使用的是C的*“地址”*运算符。该表达式&i返回变量的地址i,在这种情况下,该地址恰好是65524。由于65524表示一个地址,因此没有与之相关的符号的问题。因此,使用将其打印出来%u,后者是用于打印无符号整数的格式说明符。我们&scanf()语句中一直在使用运算符。

C语言中可用的另一个指针运算符是*,称为*“地址值”运算符。它给出了存储在特定地址的值。该“值在地址”运营商也被称为“间接”*操作。

仔细观察以下程序的输出:

#include<stdio.h>
main() 
{ 
    int i = 3 ; 
    printf("\nAddress of i = %u",&i); 
    printf("\nValueof i = %d",i); 
    printf("\nValue of i = %d",*(&i)); 
}

上面程序的输出将是:

Address of i = 65524 
Value of i = 3 
Value of i = 3

请注意,打印的值*(&i)与打印的值相同i。该表达式&i给出变量i的地址。这个地址可以收集在一个变量中,也就是说,

j = &i ;

但是请记住,j它不是任何其他整数变量一样的普通变量。它是一个变量,包含其他变量的地址(i在这种情况下)。由于j是变量,因此编译器必须在内存中为其提供空间。再一次,下面的内存映射将说明i和j的内容。

PointerMemory

如您所见,i's值是3,j's值是i's地址。但是,等等,我们不能j在没有声明的情况下在程序中使用它。并且由于j是包含地址的变量i,因此声明为,

int *j ;

该声明告诉编译器j将用于存储整数值的地址。换句话说j指向整数。我们如何证明*声明中使用,

int *j ;

让我们来了解的含义*。它代表*“地址值”*。因此,int *j这意味着包含在其中的地址的值j是int。

8.4范例

这是一个演示我们一直在讨论的关系的程序。

#include<stdio.h>
main( ) 
{ 
    int i = 3 ; 
    int *j ; 
    j = &i ; 
    printf ("\nAddress of i = %u",&i) ; 
    printf ("\nAddress of i = %u",j) ; 
    printf ("\nAddress of j = %u",&j) ; 
    printf ("\nValue of j = %u",j) ; 
    printf ("\nValue of i = %d",i) ; 
    printf ("\nValue of i = %d",*(&i)) ; 
    printf ("\nValue of i = %d",*j) ; 
} 

上面程序的输出为:

Address of i = 65524 
Address of i = 65524 
Address of j = 65522 
Value of j = 65524 
Value of i = 3 
Value of i = 3 
Value of i = 3 


9.结构体

结构体是一个或多个变量(可能是不同类型)的集合,以单个名称分组在一起以方便处理。

9.1声明结构体

结构体声明语句的一般形式如下:

struct <structure name> 
{ 
    structure element 1; 
    structure element 2; 
    structure element 3; 
    ...... 
    ......  
    structure element n;
};

一旦定义了新的结构体数据类型,就可以声明一个或多个变量为该类型。

例如,变量b1,b2,b3可以声明为struct book类型,

struct book 
{ 
    char name; 
    float price; 
    int pages; 
}; 

如,

struct book b1, b2, b3 ; 

该语句在内存中留出空间。它提供了空间来容纳结构体中的所有元素(在本例中为7个字节),一个用于名称,四个用于价格,两个用于页面。这些字节始终位于相邻的存储器位置。

与主变量和数组一样,结构体变量也可以在声明它们的地方进行初始化。使用的格式与用于初始化数组的格式非常相似。

struct book 
{ 
    char name[10]; 
    float price; 
    int pages; 
};
 
struct book b1 = { "Basic", 130.00, 550 } ; 
struct book b2 = { "Physics", 150.80, 800 } ;

9.2访问结构体元素

在数组中,我们可以使用下标访问数组的各个元素。结构体使用不同的方案。他们使用点(.)运算符。因此,pages我们必须使用书本结构体中定义的结构体,

b1.pages 

同样地,price我们将使用

b1.price 

请注意,在点之前必须始终有一个结构体变量,而在点之后必须始终有一个结构体元素。

9.3范例

以下示例说明了此数据类型的用法。

#include<stdio.h> 
main() 
{ 
    struct book 
    { 
        char name; 
        float price; 
        int pages; 
    }; 
    struct book b1, b2, b3 ;
     
    printf("\nEnter names, prices & no. of pages of 3 books\n"); 
    scanf("%c %f %d", &b1.name, &b1.price, &b1.pages); 
    scanf("%c %f %d", &b2.name, &b2.price, &b2.pages); 
    scanf("%c %f %d", &b3.name, &b3.price, &b3.pages);
     
    printf("\n\nAnd this is what you entered"); 
    printf("\n%c %f %d", b1.name, b1.price, b1.pages); 
    printf("\n%c %f %d", b2.name, b2.price, b2.pages); 
    printf("\n%c %f %d", b3.name, b3.price, b3.pages); 
} 

这是输出…

Enter names, prices and no. of pages of 3 books 
A 100.00 354 
C 256.50 682 
F 233.70 512 

And this is what you entered 
A 100.000000 354 
C 256.500000 682 
F 233.700000 512

9.4小结

  • 当我们希望将不同的数据存储在一起时,通常使用一种结构体。
  • 可以使用点(.)运算符通过结构体变量访问结构体元素。
  • 可以使用箭头(->)运算符通过指向结构体的指针来访问结构体元素。
  • 可以使用Assignment(=)运算符将一个结构体变量的所有元素分配给另一个结构体变量。
  • 可以通过值或地址将结构体变量传递给函数。
  • 可以创建一个结构体数组


10.文件

10.1文件指针

在C语言中有多种使用文件的方法。最直接的文件使用方法是通过文件指针。

FILE *fp; 

fp 是指向文件的指针。

类型FILE不是基本类型,而是在头文件中定义stdio.h,该文件必须包含在程序中。

10.2打开文件

fp = fopen(filename, mode);

文件名和模式都是字符串。

该模式可以是

  • r -阅读
  • w -写入,覆盖文件(如果存在)
  • a -写,但是附加而不是覆盖
  • r+ -读写,如果文件存在则不要销毁
  • w+ -读写,但是覆盖文件(如果存在)
  • a+ -读写,但是追加而不是覆盖
  • b -可以附加到上述任何内容中,以强制以二进制模式而不是文本模式打开文件
  • fp = fopen("data.dat","a"); -将打开磁盘文件data.dat进行写入,并且写入的所有信息都将附加到该文件中。

ANSI C Rationale中的以下有用表格列出了打开文件的不同模式的不同操作和要求:

filehanding

fopen``NULL如果无法以请求的模式打开文件,则返回。在尝试访问文件之前,应检查返回的值。以下代码显示了如何检查fopen返回的值。当无法打开文件时,将显示一条适当的错误消息,并暂停程序。在大多数情况下,这是不合适的,相反,应该为用户提供重新输入文件名的机会。

#include <stdio.h>
int main()
{
    char filename[80];
    FILE *fp;
    printf("File to be opened? ");
    scanf("%79s", filename);
    fp = fopen(filename,"r");
    if (fp == NULL)
    {
        fpri ntf(stderr, "Unable to open file %s\n", filename);
        return 1; /* Exit to operating system */
    }
    //code that accesses the contents of the file
    return 0;
}

使用以下库函数执行顺序文件访问。

  • fprintf(fp, formatstring , ...) -打印到文件
  • fscanf(fp, formatstring , ...) -从文件中读取
  • getc(fp) -从文件中获取字符
  • putc(c, fp) -将字符放入文件中
  • ungetc(c, fp) -将一个字符放回文件中(保证只有一个字符能够被推回)
  • fopen( filename , mode) -打开文件
  • fclose(fp) -关闭文件

标准头文件stdio.h中定义了三个文件指针常数stdinstdout并且stderr对于标准输入,输出和错误流。将错误消息写入标准错误流被认为是一种好习惯。

使用此fprintf()功能可以执行以下操作:

fprintf(stderr,"ERROR: unable to open file %s\n", filename);

函数fscanf()getc()用于顺序访问文件,即后续读取将在读取的数据之后读取数据,最终您将到达文件的末尾。如果要在文件中前后移动,或从起点,终点或当前位置跳转到特定位置,请使用此fseek()功能(尽管必须以二进制模式打开文件)。该函数ftell()报告从文件开头的当前偏移量。

如果你想混合读取和写入同一个文件,从阅读到写作每个开关(反之亦然)mustbe通过调用之前到fseek()fsetpos()rewind()fflush()。如果需要保留在文件中的同一位置,请使用fflush()fseek(fp,0L,SEEK_CUR)将其从当前位置移0个字节!



11.命令行参数

我们main()在命令提示符处传递的参数称为命令行参数。main的完整声明如下所示:

int main (int argc, char *argv[])

该函数main()可以有两个参数,传统上称为argcargv。这些中,argv是一个指针到字符串的数组和argc是一个int其值等于字符串其数量argv分。执行程序时,命令行上的字符串将传递给main()。更准确地说,命令行中的字符串存储在内存中,第一个字符串的argv[0]地址存储在,第二个字符串的地址存储在argv[1],等等。参数argc设置为命令行上给定的字符串数。

例如,在我们的示例程序中,如果在命令提示符下给出,

filecopy PR1.C PR2.C

然后,

argc 将包含3

  • argv[0]-将包含字符串*“ filecopy”的*基地址
  • argv[1]-包含字符串*“ PR1.C”的*基地址
  • argv[2]-将包含字符串*“ PR2.C”的*基地址

来源:
C Programming Language Cheat Sheet https://developerinsider.co/c-programming-language-cheat-sheet/

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Ha-Ha-Interesting

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值