C语言-02_C语言基础


目标

  • 1、C语言程序的基本结构

  • 2、C语言关键字和标识符

  • 3、C语言的数据类型和变量的以及存储类型

  • 4、C语言中的标准输入输出函数

  • 5、C语言中的常量

  • 6、C语言中的运算符

  • 7、C语言中的表达式

一、C语言程序的基本结构

1.1 基本结构

C语言程序的基本结构包括以下几个部分:

1.预处理指令:这些编译器指令,通常放在程序的开头,#include <stdio.h> 。预处理器指令以#字符开始,最常见的#include,用于包含其他的头文件。
2.函数:C程序中至少包含一个函数,即main()函数。main()函数是程序执行的起点。此外,一个C程序中可以包含其他的函数,这些函数可以自己定义也可以是从库中调用。

3.变量:变量用于存储数据。在C语言中,每个变量都有特有类型。

4.语句&表达式:语句是执行特定任务的语句,如赋值语句、调用函数等,表达式则是计算产生值的计算公式。

5.注释:注释用于解释程序的功能,对代码进行解释和描述。在C语言中,注释可以用/* 注释内容 */或者//注释内容来书写。
在这里插入图片描述
实例程序

#include <stdio.h>   //预处理指令

/* 这是一个简单的C程序 */
int main()			//函数开始
{
	int a = 10;  	//定义变量和赋值语句
	printf("Hello World");	//语句:调用printf函数
	return 0;		//语句:返回,结束main函数
}					//函数结束

在这个程序中:

#include <stdio.h>是预处理指令,用于包含stdio.h头文件。

main()是函数,程序的入口。

int a = 10为变量赋值语句。

printf是一个函数调用该语句,用于输出字符以及字符串和变量a的值。

1.2 gcc编译器

1.2.1 gcc编译流程

gcc的编译流程分为4个步骤:

①预处理

②编译

③汇编

④链接

支持以下后缀:

.c C语言源代码

.h 程序中所包含的头文件

.i 已经预处理过的C源代码文件

.s 汇编语言源代码文件

.o 汇编后的目标文件


接下通过一个实例程序来对gcc进行讲解,实例程序如下,分为两个文件一个hello.c和hello.h:

//hello.c//
#include <stdio.h>
#include "hello.h"
int main()
{
    u8 i;
    printf("%ld\n",sizeof(i));
    return 0;
}
#ifndef __HELLO_H_
#define __HELLO_H_

typedef unsigned int u8;


#endif

预处理阶段:

gcc的选项"-E"可以使用编译器在预处理结束时就停止编译,选项-o是指定gcc输出结果,

gcc -E -o [目标文件] [编译文件]
gcc -E -o hello.i hello.c

编译阶段:

编译器在预处理结束后开始使用,gcc首先要检查代码的规范性、是否还有语法错误等,在检查无误后,就开始把代码翻译成汇编语言。

gcc -S -o [目标文件] [编译文件]
gcc -S -o hello.s hello.i

汇编阶段:

汇编的过程就是将.s文件生成生产目标文件,我们在使用-c就可以看到汇编的代码转换成为.o的二进制目标代码了。

gcc -c -o [目标文件] [编译文件]
gcc -c -o hello.o hello.s

链接阶段:

成功编译后,就进入到了链接阶段,在这里涉及到一个很重要的概念:函数库。

当我们使用一些库文件时,则会

二、C语言关键字和标识符

2.1关键字

关键字:关键字是C语言中预先定义并保留的特殊单词,每个关键字在C语言中都有特定的含义和用途。我们在编写C程序时不能将关键字作为变量名或者其他标识名。以下是C语言的关键字:

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

2.2标识符

标识符:是程序编写过程中的变量名、函数名、数组名等元素定义的名称。标识符的命名需要遵循以下规则:

1.标识符的第一个字符必须是字母(大写或小写)或者下划线"_"。

2.后续的字符可以是字母、数字或下划线。

3.关键字不能作为标识符。

4.标识符在C语言中是区分大小写的。

//正确
int myVar;
double _myVar;
unsigned long int var123;
void myFunction();
//错误
int 123var;    // 以数字开头
double double; // 使用了关键字

随堂小测:

int score;              // 正确
double _maxValue;       // 正确
char response1;         // 正确
int 7score;             // 错误,名称不能以数字开头
void print_score();     // 正确
double double;          // 错误,不能使用关键字作为名称
char a b;               // 错误,名称中不能有空格
int number of students; // 错误,名称中不能有空格
void PrintScore();      // 正确,但不符合C语言的常见命名习惯,函数名通常全部小写,如print_score
int NumberOfStudents;   // 正确,但不符合C语言的常见命名习惯,更常见的是使用下划线,如number_of_students

三、C语言的数据类型和变量的以及存储类型

3.1 计算机数据的表示

3.1.1 进制

计算机中数据通常以二进制形式表示,但根据需要,它们可以进一步划分为不同的类型。以下是常见的计算机中数据表示的方式:

3.1.1.1 数值数据
  1. 二进制:这是计算机最基础的数据表示方式,所有的信息都可以转换为0和1。这是因为计算机的硬件电路(例如,开关和逻辑门)最适合处理二进制系统。
  2. 十进制:尽管计算机内部使用二进制,但在显示和输入数据时,通常使用我们更熟悉的十进制系统。计算机内部会将十进制数字转换为二进制,进行计算后再转回十进制。
  3. 十六进制:十六进制被广泛用于计算机科学和编程,因为它可以更简洁地表示二进制数。每个十六进制数字可以精确地表示四个二进制位,这使得二进制数和十六进制数之间的转换非常直接。
3.1.1.1 非数值数据
  1. ASCII:ASCII是一种将字符(如字母、数字和特殊符号)映射到二进制数的标准。例如,在ASCII中,大写字母A的编码是65,对应的二进制是1000001。
  2. 浮点数:计算机使用浮点数表示法来存储和操作实数。IEEE 754标准定义了浮点数的表示方式和操作,这是现代计算机最广泛使用的浮点数标准。
3.1.2 源码、反码、补码

原码、反码和补码是用于表示和存储二进制有符号整数的方式。它们都用最高位(通常是左边的位)表示符号,0表示正,1表示负。

原码:原码就是符号位加上真值的绝对值,即用第一位表示符号,其余位表示值。比如如果是8位二进制:

  • [+1]原 = 0000 0001
  • [-1]原 = 1000 0001

反码:正数的反码是其本身,负数的反码是在其原码的基础上, 符号位不变,其余各位取反。

  • [+1]反 = 0000 0001
  • [-1]反 = 1111 1110

补码:正数的补码就是其本身,负数的补码是在其原码的基础上,符号位不变,其余各个位取反,然后最后一位加1。

  • [+1]补 = 0000 0001
  • [-1]补 = 1111 1111

3.2 数据类型

在C语言中,数据类型指的是用于声明不同类型的变量或函数的一个广泛的系统。变量的类型决定了变量存储的大小和布局,该范围内的值都可以存储在内存中,以及该位置可以参与的运算。

C语言中的数据类型主要可以分为以下几类:

3.2.1 基本类型:它们是算术类型,包括两种类型:整数类型和浮点类型。
  • 整数类型包括:charshort intintlong intlong long int
  • 浮点类型包括:floatdoublelong double
3.2.2 枚举类型

枚举类型(Enumeration)是C语言的一种派生数据类型,它是由用户定义的若干个整型常量的集合。如果一个变量有几种可能的值,但是这些值是已知且数量不多的,那么就可以将它们定义为枚举类型。枚举类型提供了一种有效的方式来创建和使用常量集合。

枚举类型的定义方式如下:

enum 枚举名 { 枚举元素1, 枚举元素2, ..., 枚举元素n };

例如:

enum Day {SUNDAY, MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY};

在这个例子中,Day是一个新的枚举类型,它包含了七个枚举元素,分别表示一周的七天。这些元素默认对应的值为0到6,你也可以在定义时指定它们的值。

例如:

enum Day {SUNDAY = 1, MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY};

在这个例子中,SUNDAY对应的值为1,而其它的元素则依次对应2到7。

使用枚举类型可以提高代码的可读性和易于维护性。

3.2.3 void类型:在C语言中,void类型主要有三种用途:

函数返回值为空:当一个函数不需要返回任何值时,我们会使用void作为该函数的返回类型。例如:

void printHelloWorld() {
    printf("Hello, World!\n");
}

在这个例子中,printHelloWorld函数没有返回值,它的任务就是打印一条消息。

函数参数为空:当一个函数不需要任何参数时,我们会使用void在函数原型中表明这一点。例如:

int getFive(void) {
    return 5;
}

在这个例子中,getFive函数不接收任何参数,而返回值是一个整数。

泛型指针:void还可以用来定义一种特殊的指针,称为"泛型指针",可以指向任意类型的数据。例如:

void *ptr;
  1. 这里的ptr是一个可以指向任何类型的指针。需要注意的是,void指针不能直接进行解引用操作(即获取指针指向的值),需要先将其转换为具体的数据类型的指针。

注意,void不是说这个函数/变量/指针"没有类型",而是说它"没有特定的类型"。例如,void指针实际上并没有丧失指针的本质——存储和使用内存地址的能力,只不过它不知道自己所指向的数据的类型是什么而已。

转换方式:

使用void指针时,往往需要通过类型转换(Type Casting)来将void指针转换为指向特定类型的指针。下面是一个例子:

#include <stdio.h>

int main() {
    int x = 10;
    double y = 20.0;

    // 创建一个void指针,它可以指向任何类型的数据
    void *ptr;

    // 指向整数
    ptr = &x;
    printf("整数x: %d\n", *(int *)ptr);

    // 指向双精度浮点数
    ptr = &y;
    printf("双精度浮点数y: %.2lf\n", *(double *)ptr);

    return 0;
}

在这个例子中,void指针首先被设置为指向整数x的地址,然后我们使用(int *)ptr来将void指针转换为int类型的指针,然后解引用它以获取整数的值。同样的,我们也将void指针设置为指向双精度浮点数y的地址,然后使用(double *)ptr将void指针转换为double类型的指针,然后解引用它以获取浮点数的值。

请注意,在解引用void指针之前,必须先将其转换为其他指针类型。否则,编译器将不知道如何解释存储在指定地址的数据。

3.2.4 派生类型

在C语言中,派生类型是基于基础数据类型(如int,char,float,double等)创建的数据类型。这些派生类型包括:

  1. 数组(Array):数组是具有固定数量,且具有相同数据类型的元素的集合。例如,我们可以有一个整型数组,其所有元素都是整数。

     
    int array[10];
    
  2. 函数(Function):函数是一组执行特定任务的语句。函数可以有参数,也可以没有参数,可以返回一个值,也可以不返回。

     void function_name() {
        // 函数体
    }
    
  3. 指针(Pointer):指针是一个变量,其值为另一个变量的地址,即,直接地址或内存位置。

     
    int* p;
    

    这些派生类型为C语言的编程提供了巨大的灵活性。

3.2.5 复合类型

复合类型是指可以包含其他类型的类型。在C语言中,我们通常将结构体(struct)和联合体(union)看作复合类型。这是因为它们可以将不同的数据类型组合在一起。

  1. 结构体(struct):结构体是由多个不同类型的变量组成的一种数据类型。结构体可以包含任意类型的数据,包括其他结构体,数组,甚至是指针。
 struct Student {
    char name[50];
    int age;
    float grade;
};

在上述代码中,我们定义了一个名为"Student"的结构体,它包含一个字符数组(用于存储姓名),一个整数(用于存储年龄)和一个浮点数(用于存储成绩)。

  1. 联合体(union):联合体和结构体有些相似,不过联合体在任何时刻只能存储其中一个成员的值。换句话说,联合体的所有成员都共享同一内存位置。
 union Data {
    int i;
    float f;
    char str[20];
};

在上述代码中,我们定义了一个名为"Data"的联合体,它可以存储一个整数、一个浮点数或一个字符数组,但是这三个不能同时存储。如果你在联合体变量中存储了一个新的值,它将覆盖先前的值。

3.3存储类型

C语言中的存储类型用于描述变量或函数的生命周期(可见性和范围)和存储位置。以下是主要的存储类型:

  1. 自动存储类型(auto): 这是所有局部变量的默认存储类型。在函数内定义的变量,当函数调用结束后,它们会自动销毁。它们在栈中存储。
  2. 寄存器存储类型(register): 这类变量存储在寄存器中,而不是RAM中。这样可以更快地访问这些变量。但是,寄存器的空间有限,因此只能用于小型且频繁使用的变量。
  3. 静态存储类型(static): 这类变量在程序的整个生 命周期中保持存在,而不是在每次它们的作用域被执行时创建和销毁。如果变量在函数内部被声明为static,那么它只会在第一次函数调用时被初始化,之后即使函数结束也不会被销毁。如果变量在函数外部被声明为static,那么它的作用范围就会被限制在声明它的文件中。
  4. 外部存储类型(extern): 用extern声明的变量表示变量在程序的其他地方已经被定义。这对于在多个文件中共享变量和函数非常有用。

每一种存储类型都有其特定的属性,能在不同的场景下提供方便。理解和正确使用它们是成为高效C程序员的重要步骤。

四、C语言中的标准输入输出函数


4.2 缓冲区

在这里插入图片描述

  1. 输入缓冲区: 当我们在键盘上敲击字符时,这些字符首先被存储在输入缓冲区中。当我们按下回车键或者缓冲区满时,存储在缓冲区的数据才会被送入程序处理。
  2. 输出缓冲区: 当我们执行一个输出函数(如printf)时,数据首先被放入输出缓冲区,直到缓冲区满或者调用刷新缓冲区函数(如fflush)时,才会真正输出到屏幕上。

4.2 输入函数:

  1. scanf():此函数用于从标准输入(键盘)读取并转换。它需要一个格式字符串,该字符串确定了如何读取输入。
  2. getchar():此函数从标准输入读取并返回一个字符。
  3. gets():此函数从指定的流中读取并存储一行字符。该行以空字符终止。

4.3 输出函数:

  1. printf():此函数用于在标准输出(显示器)上打印格式化的输出。它需要一个格式字符串,该字符串确定了如何打印变量的值。
  2. putchar():此函数将一个字符写入标准输出。
  3. puts():此函数写一个字符串和一个尾随的换行符到标准输出。
4.4 单字符I/O:getcahr() 和 putchar()
4.4.1 getcahr()

函数:getchar()

头文件:#include <stdio.h>

函数原型:int getchar(void);

功能:用于从标准输入设备(通常是键盘)读取一个字符。

参数:无参数

返回值:返回读取的字符。如果发生错误则返回EOF。


getcahr()函数用于从标准输入(通常是键盘)获取一个字符。它不需要任何参数,并返回读取的字符,如果输入流结束或出现错误getchar()将返回EOF。使用他的一个基本例子如下:

#include <stdio.h>

int main()
{
    char c;
    printf("请输入一个字符");
    c = getchar();
    printf("你输入的字符为:%c\n",c);
    return 0;
}

4.4.2 putcahr()

函数:putchar()

头文件:#include <stdio.h>

函数原型:int putchar(int c);

功能:将指定的字符输出到标准输出设备(通常是显示器)。

参数:c – 需要输出的字符

返回值:如果输出成功,则返回输出的字符。如果发生错误,则返回EOF。


putcahr()函数用于向标准输出(通常是屏幕)写入一个字符。它需要一个字符作为参数,并返回写入的字符。如果发生错误,putchar()将返回EOF。以下是一个例子:

#include <stdio.h>

int main()
{
	char c = 'a';
    putchar(c);
	return 0;
}

语法格式

	putchar(字符型变量);
	char a = 'c';
	//1)输出字符到屏幕上
	putchar(a);  
	putchar('c');  
	//2)输出ascii码值为10的字符到屏幕上    字符-》ascii 存储起来。
	putchar(10);    
	//3)把字符型变量a的值输出到屏幕上  
	putchar(a);   
	//4)将ascii码值为65的字符输出到屏幕上    
	putchar(65);  
	//5)把转义字符的功能输出到屏幕上。  
	putchar('\n');
	//6)大A
	putchar('\101');
	putchar('\x41');
	//7)小写的a  :   用十进制数字代替ascii'字符  用八进制书代替ascii字符。。
	putchar(97);
4.5 多字符I/O:printf() 和 scanf()
4.5.1 printf()函数

函数:printf()

头文件:#include <stdio.h>

函数原型:int printf(const char *format, ...);

功能:格式化输出数据到标准输出设备。

参数:*format – 控制格式和输出的字符串,... – 可变参数,根据格式字符串来传递。

返回值:返回输出的字符数,如果发生错误则返回一个负值。


printf()scanf() 是C语言中最常用的两个多字符I/O函数,它们分别用于格式化输出和输入。

printf()函数用于将格式化的数据输出到标准输出设备(通常是屏幕)。它可以输出各种类型的数据,如整数、浮点数、字符和字符串。其函数原型为:

int printf(const char *format, ...);

这里的 format 是一个格式化字符串,它可以包含一些格式化说明符,如 %d(用于整数)、%f(用于浮点数)、%s(用于字符串)等。

4.5.2 scanf()

函数:scanf()

头文件:#include <stdio.h>

函数原型:int scanf(const char *format, ...);

功能:从标准输入设备读取数据,根据指定的格式进行解析。

参数:*format – 控制读取格式的字符串,... – 可变参数,接收读取的数据。

返回值:返回成功匹配和读取的项目数,如果达到文件末尾则返回EOF。


scanf()函数用于从标准输入设备(通常是键盘)读取格式化的数据。它可以读取各种类型的数据,如整数、浮点数、字符和字符串。其函数原型为:

int scanf(const char *format, ...);

实例程序

#include <stdio.h>

int main() {
    int a;
    float b;
    char str[100];

    // 使用 scanf 读取用户输入
    printf("请输入一个整数:");
    scanf("%d", &a);
    printf("请输入一个浮点数:");
    scanf("%f", &b);
    printf("请输入一个字符串:");
    scanf("%s", str);

    // 使用 printf 输出用户输入的数据
    printf("你输入的整数是:%d\n", a);
    printf("你输入的浮点数是:%.2f\n", b);
    printf("你输入的字符串是:%s\n", str);

    return 0;
}

4.5.3 printf描述符

格式符号

格式说明符描述
%d以十进制形式输出带符号整数
%u以十进制形式输出无符号整数
%f以小数形式输出单、双精度浮点数
%e, %E以指数形式输出单、双精度浮点数
%g, %G根据值的不同,自动选择%f%e%E格式输出单、双精度浮点数
%c输出一个字符
%s输出一个字符串
%p输出一个指针(以十六进制形式输出指针的值)
%x, %X以十六进制形式输出无符号整数
%o以八进制形式输出无符号整数
%ld输出长整型
%lu输出无符号长整型

格式的描述符

修饰符示例代码输出描述
-printf("%-10d", 123);123左对齐输出
+printf("%+d", 123);+123在正数前面显示加号
空格printf("% d", 123);123在正数前面显示空格
#printf("%#o", 123);0173以八进制显示数值
0printf("%010d", 123);0000000123左边用0填充
数字printf("%10d", 123);123设定字段宽度为10
.printf("%.2f", 123.456);123.46保留2位小数
lprintf("%ld", 123456789L);123456789长整型
llprintf("%lld", 123456789012345LL);123456789012345长长整型
hshort s = 123; printf("%hd", s);123短整型
hhsigned char c = 123; printf("%hhd", c);123有符号字符
Lprintf("%Lf", 123456.789L);123456.789000长双精度浮点数
4.5.4 scanf描述符

描述符

描述符作用示例
%d匹配带符号的十进制整数scanf("%d", &num);
%i匹配带符号的整数,可以是十进制、八进制、十六进制scanf("%i", &num);
%o匹配八进制整数scanf("%o", &num);
%u匹配无符号十进制整数scanf("%u", &num);
%x %X匹配无符号十六进制整数scanf("%x", &num);
%f匹配浮点数scanf("%f", &num);
%e %E匹配浮点数,可以是指数形式scanf("%e", &num);
%g %G根据数值和精度选择 %f%e (%E)scanf("%g", &num);
%c匹配一个字符scanf("%c", &char);
%s匹配字符串,遇到空格、制表符或换行符结束scanf("%s", str);
%p匹配指针scanf("%p", &ptr);

注意:scanf中的 %sprintf 中的 %s 的行为是完全不同的。printf 中的 %s 会输出字符串直到遇到空字符 '\0',而 scanf 中的 %s 会读取输入直到遇到一个空格、制表符或换行符。

4.6 字符串I/O:puts()和gets()
4.6.1 puts

函数:puts()

头文件:#include <stdio.h>

函数原型:int puts(const char *s);

功能:将字符串s和一个尾随的换行符写入到标准输出(stdout)。

参数:*s – 指向一个字符数组的指针,该数组包含了一个空字符(‘\0’)来结束输入的字符串。

返回值:如果执行成功,返回非负值,如果发生错误则返回EOF。


4.6.1 gets

函数:gets()

头文件:#include <stdio.h>

函数原型:char *gets(char *s);

功能:从标准输入(stdin)读取一行,并把它保存到str所指向的字符数组,它会在读取的行尾自动加上null字符,但它不会保留换行符。

参数:*s – 一个数组,该数组用来存储读取的字符串,直到一个换行符(‘\n’)或EOF。

返回值:如果执行成功,返回str。如果发生错误或读到文件的末尾且未读到任何字符,返回NULL。

注意:C11已经从库函数中删除了gets(),使用该函数可能会存在安全风险,比如缓冲区溢出等问题,通常建议使用fgets()替代。


五、C语言中的常量


#define LENGTH 10
const int WIDTH = 5;


比较

  • #define是预处理器宏,而const是语言自身的一部分。
  • const可以用于更复杂的类型,比如对象,而#define只能用于基本类型。
  • #define不分配存储空间,而const通常分配存储空间(例如在RAM中)。
  • const常量的类型和大小都是固定的,但#define预处理器宏没有固定的类型或大小。

六、C语言中的运算符

运算符类别运算符
算术运算符+,-,*,/,%
赋值运算符=,+=,-=,*=,/=,%=
关系运算符==,!=,>,<,>=,<=
逻辑运算符&&,
位运算符&,|,^,~,<<,>>
条件运算符? :
自增自减运算符++,–
逗号运算符,
指针运算符*,&
sizeof运算符sizeof

6.1 算术运算符

#include <stdio.h>

int main() {
    int a = 10;
    int b = 20;
    printf("a + b = %d\n", a + b);  // 30
    printf("a - b = %d\n", a - b);  // -10
    printf("a * b = %d\n", a * b);  // 200
    printf("b / a = %d\n", b / a);  // 2
    printf("b %% a = %d\n", b % a);  // 0
    return 0;
}

6.2 赋值运算符

#include <stdio.h>

int main() {
    int a = 10;
    a += 20;  // a = a + 20
    printf("a = %d\n", a);  // 30

    a -= 5;  // a = a - 5
    printf("a = %d\n", a);  // 25

    a *= 2;  // a = a * 2
    printf("a = %d\n", a);  // 50

    a /= 10;  // a = a / 10
    printf("a = %d\n", a);  // 5

    a %= 3;  // a = a % 3
    printf("a = %d\n", a);  // 2
    return 0;
}

6.3 关系运算符

关系运算符在C语言中用于比较两个值,结果为一个布尔值(0或1)。如果比较的条件成立,那么结果为1,否则为0。以下是C语言中的关系运算符:

#include <stdio.h>

int main() {
    int a = 10;
    int b = 20;
    printf("a > b: %d\n", a > b);  // 0 (false)
    printf("a < b: %d\n", a < b);  // 1 (true)
    printf("a >= b: %d\n", a >= b);  // 0 (false)
    printf("a <= b: %d\n", a <= b);  // 1 (true)
    printf("a == b: %d\n", a == b);  // 0 (false)
    printf("a != b: %d\n", a != b);  // 1 (true)
    return 0;
}

6.4 逻辑运算符

逻辑运算符用于链接和操作布尔值(真或假,即1或0)。C语言中的逻辑运算符包括“与”(&&),“或”(||)和“非”(!)。以下是这些运算符的解释:

**1.**逻辑与运算符(&&):当两边的操作数都为真(非零)时,结果才为真。如果任一操作数为假(即零),结果就为假。

例如:

int a = 1;
int b = 0;
int result = a || b; // result 的值为 1,因为 a 为非零

**2.**逻辑或运算符(||):只要两个操作数中的任一操作数为真,结果就为真。只有当两个操作数都为假,结果才为假。

例如:

int a = 1;
int b = 0;
int result = a || b; // result 的值为 1,因为 a 为非零

**3.**逻辑非运算符(!):这是一个单目运算符,对一个布尔值进行取反操作。如果操作数为真,结果为假,反之亦然。

例如:

int a = 1;
int result = !a; // result 的值为 0,因为 a 为真(非零)

示例:

#include <stdio.h>

int main() {
    int a = 1;
    int b = 0;
    printf("a && b: %d\n", a && b);  // 0 (false)
    printf("a || b: %d\n", a || b);  // 1 (true)
    printf("!a: %d\n", !a);  // 0 (false)
    printf("!b: %d\n", !b);  // 1 (true)
    return 0;
}

6.5 位运算符

位运算符直接对整数在内存中的二进制位进行操作。这些运算符包括:按位与(&)、按位或(|)、按位异或(^)、取反(~)、左移(<<)和右移(>>)。下面详细解释每一种:

**1.**按位与运算符(&):如果两个相应的二进制位都为1,则结果为1,否则为0。

例如:

int a = 60;  // 60的二进制是 0011 1100
int b = 13;  // 13的二进制是 0000 1101
int result = a & b;  // result 的值为 12,二进制为 0000 1100

**2.**按位或运算符(|):如果两个相应的二进制位中至少有一个1,则结果为1,否则为0。

例如:

int a = 60;  // 60的二进制是 0011 1100
int b = 13;  // 13的二进制是 0000 1101
int result = a | b;  // result 的值为 61,二进制为 0011 1101

**3.**按位异或运算符(^):如果两个相应的二进制位值相同,则结果为0,否则为1。

例如:

int a = 60;  // 60的二进制是 0011 1100
int b = 13;  // 13的二进制是 0000 1101
int result = a ^ b;  // result 的值为 49,二进制为 0011 0001

**4.**取反运算符(~):对数的每一位取反,0变1,1变0。

例如:

int a = 60;  // 60的二进制是 0011 1100
int result = ~a;  // result 的值为 -61,二进制为 1100 0011

**5.**左移运算符(<<):把位向左移动指定的位数,右边用0填充。

例如:

int a = 15;  // 15的二进制是 0000 1111
int result = a << 2;  // result 的值为 60,二进制为 0011 1100

**6.**右移运算符(>>):把位向右移动指定的位数,左边的空位如何填充取决于数字的符号(正数左边填充0,负数左边填充1)。

例如:

int a = 15;  // 15的二进制是 0000 1111
int result = a >> 2;  // result 的值为 3,二进制为 0000 0011

实例:

#include <stdio.h>

int main() {
    int a = 60; /* 60 = 0011 1100 */
    int b = 13; /* 13 = 0000 1101 */
    int result;

    result = a & b; /* 12 = 0000 1100 */
    printf("result = a & b: %d\n", result);

    result = a | b; /* 61 = 0011 1101 */
    printf("result = a | b: %d\n", result);

    result = a ^ b; /* 49 = 0011 0001 */
    printf("result = a ^ b: %d\n", result);

    result = ~a; /*-61 = 1100 0011 */
    printf("result = ~a: %d\n", result);

    result = a << 2; /* 240 = 1111 0000 */
    printf("result = a << 2: %d\n", result);

    result = a >> 2; /* 15 = 0000 1111 */
    printf("result = a >> 2: %d\n", result);

    return 0;
}

6.6 条件运算符

条件运算符也称为三元运算符,其格式为:

条件表达式 ? 表达式1 : 表达式2

三元运算符的执行过程是这样的:

  • 首先,程序会计算条件表达式的值
  • 如果条件表达式的值为真(非0),那么程序会计算调大是1

例如:

#include <stdio.h>

int main() {
    int a = 10;
    int b = 20;
    int max = a > b ? a : b;
    printf("最大的数是:%d\n", max);
    return 0;
}
#include <stdio.h>

int main() {
    int a = 10;
    int b = (a == 1) ? 20 : 30;
    printf("b is %d\n", b);  // b is 30
    return 0;
}

6.7 自增自减运算符

C语言中的自增(++)和自减(–)运算符用于增加或减少变量的值。这些运算符有前缀和后缀两种形式,对应着不同的操作顺序。

  • 前缀自增/自减:先进行自增/自减操作,然后再进行其他操作。例如,++a--a
  • 后缀自增/自减:先进行其他操作,然后再进行自增/自减操作。例如,a++a--
int a = 10;
int b = ++a; // 先增加a的值,然后赋值给b。此时a和b都为11。

a = 10; // 重置a的值为10
b = a++; // 先将a的值赋给b,然后再增加a的值。此时a为11,b为10。

6.8 逗号运算符

逗号运算符在C语言中被用来链接两个或更多的独立的表达式,从而使它们能在一行内执行。逗号运算符有两个主要的特点:

  1. 从左至右的顺序执行:逗号运算符的所有操作数都按从左到右的顺序进行计算。
  2. 返回值:逗号运算符的返回值是最后一个表达式的值。

让我们看一个简单的例子来更好地理解这个概念:

#include <stdio.h>

int main() {
   int a, b;

   a = (b=4, b+2); 

   printf("a 的值是 %d\n", a );

   return 0;
}

在上述代码中,逗号运算符将两个表达式b=4b+2链接在一起。首先,b=4被执行,然后执行b+2,最后,b+2的结果被赋给变量a。因此,a的最终值为6。

请注意,虽然逗号运算符可以将多个表达式链接在一起,但过度使用可能会使代码难以理解和维护。在编程时,最好尽可能地保持代码的清晰和简洁。

6.9 指针运算符

在C语言中,我们主要有4个指针运算符,它们是:

  1. *(解引用运算符):这个运算符返回指针所指向的值。例如,如果p是一个指向整数的指针,并且该整数的值是10,那么*p将返回10。

    int a = 10;
    int *p = &a;
    printf("%d\n", *p);  // 输出: 10
    
  2. &(取地址运算符):这个运算符返回变量的内存地址。例如,如果a是一个整数,那么&a将返回这个整数在内存中的地址。

     int a = 10;
    printf("%p\n", &a);  // 输出: a在内存中的地址
    
  3. ++--(自增和自减运算符):这些运算符用于改变指针的值,使其指向下一个或上一个内存位置。如果p是一个指向整数的指针,并且整数占用4个字节,那么p++将使p指向内存中的下一个整数,而p--将使p指向内存中的上一个整数。

    int arr[] = {10, 20, 30};
    int *p = arr;
    printf("%d\n", *p);  // 输出: 10
    p++;
    printf("%d\n", *p);  // 输出: 20
    
  4. 指针比较运算符:这些包括 ==!=<><=>=。它们可以用来比较两个指针的值(即它们所指向的内存地址)。

    int a = 10, b = 20;
    int *p1 = &a, *p2 = &b;
    
    if (p1 != p2) {
        printf("p1 and p2 point to different locations.\n");
    }
    

以上就是C语言中关于指针的主要运算符。

6.10 sizeof运算符

sizeof运算符是C语言中的一种单目运算符,用于获取一个特定类型或特定对象的大小(以字节为单位)。其可以应用于任何数据类型,包括基本类型(如int、char、double等)、派生类型(如数组、指针等)和自定义类型(如结构体、联合等)。

以下是sizeof运算符的一些使用示例:

#include <stdio.h>
int main()
{
    int a;
    double b;
    char c[10];
    
    printf("Size of int: %lu\n", sizeof(a));  // 输出: Size of int: 4
    printf("Size of double: %lu\n", sizeof(b));  // 输出: Size of double: 8
    printf("Size of char[10]: %lu\n", sizeof(c));  // 输出: Size of char[10]: 10
    
    return 0;
}

在上述代码中,sizeof(a)返回变量a的大小(以字节为单位),sizeof(b)返回变量b的大小,sizeof(c)返回数组c的大小。

需要注意的是,sizeof运算符返回的是size_t类型的值,这是一种无符号整型数据类型,所以在printf函数中应该使用%lu(代表长无符号整型)来格式化输出。

另外,你也可以使用sizeof运算符来获取一个类型的大小,而不仅仅是变量的大小。例如,sizeof(int)将返回int类型的大小,sizeof(double)将返回double类型的大小。

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
在信号处理领域,DOA(Direction of Arrival)估计是一项关键技术,主要用于确定多个信号源到达接收阵列的方向。本文将详细探讨三种ESPRIT(Estimation of Signal Parameters via Rotational Invariance Techniques)算法在DOA估计中的实现,以及它们在MATLAB环境中的具体应用。 ESPRIT算法是由Paul Kailath等人于1986年提出的,其核心思想是利用阵列数据的旋转不变性来估计信号源的角度。这种算法相比传统的 MUSIC(Multiple Signal Classification)算法具有较低的计算复杂度,且无需进行特征值分解,因此在实际应用中颇具优势。 1. 普通ESPRIT算法 普通ESPRIT算法分为两个主要步骤:构造等效旋转不变系统和估计角度。通过空间平移(如延时)构建两个子阵列,使得它们之间的关系具有旋转不变性。然后,通过对子阵列数据进行最小二乘拟合,可以得到信号源的角频率估计,进一步转换为DOA估计。 2. 常规ESPRIT算法实现 在描述中提到的`common_esprit_method1.m`和`common_esprit_method2.m`是两种不同的普通ESPRIT算法实现。它们可能在实现细节上略有差异,比如选择子阵列的方式、参数估计的策略等。MATLAB代码通常会包含预处理步骤(如数据归一化)、子阵列构造、旋转不变性矩阵的建立、最小二乘估计等部分。通过运行这两个文件,可以比较它们在估计精度和计算效率上的异同。 3. TLS_ESPRIT算法 TLS(Total Least Squares)ESPRIT是对普通ESPRIT的优化,它考虑了数据噪声的影响,提高了估计的稳健性。在TLS_ESPRIT算法中,不假设数据噪声是高斯白噪声,而是采用总最小二乘准则来拟合数据。这使得算法在噪声环境下表现更优。`TLS_esprit.m`文件应该包含了TLS_ESPRIT算法的完整实现,包括TLS估计的步骤和旋转不变性矩阵的改进处理。 在实际应用中,选择合适的ESPRIT变体取决于系统条件,例如噪声水平、信号质量以及计算资源。通过MATLAB实现,研究者和工程师可以方便地比较不同算法的效果,并根据需要进行调整和优化。同时,这些代码也为教学和学习DOA估计提供了一个直观的平台,有助于深入理解ESPRIT算法的工作原理。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值