c语言修炼秘籍【第四章】操作符

【β-Boltzmann探索策略】 𝜀是一个介于0和1之间的参数,用于控制探索和利用的比例。当𝜀较大时,智能体更倾向于探索;当𝜀较小时,智能体更倾向于利用已知信息。 阅读详情

c语言修炼秘籍【第四章】操作符

【心法】
【第零章】c语言概述
【第一章】分支与循环语句
【第二章】函数
【第三章】数组
【第四章】操作符
【第五章】指针
【第六章】结构体
【第七章】const与c语言中一些错误代码
【禁忌秘术】
【第一式】数据的存储
【第二式】指针
【第三式】字符函数和字符串函数
【第四式】自定义类型详解(结构体、枚举、联合)
【第五式】动态内存管理
【第六式】文件操作
【第七式】程序的编译



前言

本文会对c语言中使用到的操作符进行介绍,包括

  • 算术操作符
  • 移位操作符
  • 位操作符
  • 赋值操作符
  • 单目操作符
  • 关系操作符
  • 逻辑操作符
  • 条件操作符
  • 逗号表达式
  • 下标引用、函数调用和结构成员
  • 表达式求值

一、算术操作符

+ // 加
- // 减
* // 乘
/ // 除
% // 取模
  1. 除%外,其他的几个操作符都可以作用于整数和浮点数;
  2. 对于/操作符,如果两个操作数都是整数,则执行整数除法;而只要有一个操作数为浮点数,就执行浮点数除法。
  3. %操作符的两个操作数必须都是整数。返回整除之后的余数。
#include <stdio.h>

int main()
{
	int a = 2;
	int b = 3;
	float c = 3.0f;
	printf("a / b == %f\n", a / b);
	printf("a / c == %f\n", a / c);

	return 0;
}

运行结果:
在这里插入图片描述
可以看到,2 / 3以浮点数的形式打印出来,仍然是0;2 / 3.0打印的0.666667才是正确的浮点类型结果。

二、移位操作符

>> // 右移操作符
<< // 左移操作符

// 注:移位操作符的操作数只能是整数

1.左移操作符

移位规则:
左边抛弃,右边补0


移位操作符对操作数进行操作时,是对它的保存在内存中的二进制形式进行操作。
如,5 << 1;
在这里插入图片描述
在移位过程中,5本身并没有被改变(没有进行赋值)。

2.右移操作符

右移的移位规则分成两种:

  1. 逻辑右移:右边抛弃,左边补0
  2. 算术右移:右边抛弃,左边补符号位

再这里会涉及到符号位,所以简单介绍一下,
计算机中只有两种状态1和0,所以计算机中的数都是以二进制的形式保存并使用的,
而二进制数又分为有符号数和无符号数,其中有符号数中的符号的正,负对机器而言是无法识别的,但刚好对应着两种状态,所以正,负这两种符号也被数字化了,规定0表示正,1表示负,最高位为符号位。
有符号数可分为:原码,补码,反码这三种。计算机中用补码保存二进制数

  1. 原码:
    正数:它的原码就是它自身;
    负数:符号位为1,其余位和它的绝对值的原码相同;
  1. 反码:
    正数:它的反码就是它自身;
    负数:符号位不变,其余位是它的原码取反;
  1. 补码:
    正数:它的补码就是它自身;
    负数:它的反码+1;

例如:5
它的二进制形式为:
0000000 00000000 00000000 00000101 - - 原码
0000000 00000000 00000000 00000101 - - 反码
0000000 00000000 00000000 00000101 - - 补码
正数这三种形式相同


再例如:-3
它的绝对值的原码为:00000000 00000000 00000000 00000011
10000000 00000000 00000000 00000011 - - 原码 - - 改变了符号位
11111111 11111111 11111111 11111100 - - 反码 - - 符号位之外全部把按位取反
11111111 11111111 11111111 11111101 - - 补码 - - 反码 + 1


对于逻辑移位,正如它的名字,它只负责把内存中的二进制位往右移动指定的位数,使用这种方法进行移位时,它压根不管符号位,既然不管符号位,缺位直接补0就行。
例如:-2 >> 1
首先,将-3转换为补码形式
1000000 0000000 0000000 0000010 - - 原码 - - 改变了符号位
11111111 11111111 11111111 11111101 - - 反码 - - 符号位之外全部把按位取反
11111111 11111111 11111111 11111110 - - 补码 - - 反码 + 1
右移1位:(逻辑右移)
01111111 11111111 11111111 11111111 - - 补码
此时符号位为0,机器认为它是一个正数,补码和原码相同,
-2 >> 1就变成了有符号int类型能表示的最大值;


对于算术移位,既然是算术,肯定要考虑符号了,所以以这种方式进行右移操作时,左边补位时是使用它的符号位来补的
同样的例子:-2 >> 1
右移1位:
11111111 11111111 11111111 11111111 - - 补码
转换为原码:
10000000 0000000 00000000 00000000 - - 反码
10000000 0000000 00000000 00000001 - - 原码
-2 >> 1就变成了-1;
那么我们的计算机中的右移操作是使用的哪种方式呢?
使用下面的代码进行验证:

#include <stdio.h>
#include <limits.h>

int main()
{
	int a = -2; 
	printf("a >> 1 == %d\n", a >> 1);
	printf("INT_MAX == %d\n", INT_MAX);

	return 0;
}

运行结果:
在这里插入图片描述
可以看到,a >> 1输出的结果为-1,符合算术右移的分析。

注:移位操作符不可移动负数位,这是标准未定义的操作

int a = -10;
a >> -1 // error,标准未定义

三、位操作符

& // 按位与
| // 按位或
^ // 按位异或

注意:它们的操作数必须是整数

下面代码会输出什么呢?

#include <stdio.h>

int main()
{
	int a = 5;
	int b = 6;

	printf("a & b == %d\n", a & b);
	printf("a | b == %d\n", a | b);
	printf("a ^ b == %d\n", a ^ b);

	return 0;
}

5 - - 补码 - - 00000000000000000000000000000101
6 - - 补码 - - 00000000000000000000000000000110


1 & 2
00000000000000000000000000000101
00000000000000000000000000000110
结果为:
00000000000000000000000000000100 - - 4
1 | 2
00000000000000000000000000000101
00000000000000000000000000000110
结果为:
00000000000000000000000000000111 - - 7
1 & 2
00000000000000000000000000000101
00000000000000000000000000000110
结果为:
00000000000000000000000000000011 - - 3

运行结果:
在这里插入图片描述

一道面试题:
不能创建临时变量,实现两个数的交换

#include <stdio.h>

// 方法一
// 使用临时变量进行交换
void swap1(int* num1, int* num2)
{
	int tmp = 0;
	tmp = *num1;
	*num1 = *num2;
	*num2 = tmp;
}

// 方法二
// 不使用临时变量进行交换
void swap2(int* num1, int* num2)
{
	*num1 = *num1 + *num2;
	*num2 = *num1 - *num2;
	*num1 = *num1 - *num2;
}

// 方法三
// 不使用临时变量进行交换
void swap3(int* num1, int* num2)
{
	*num1 = *num1 ^ *num2;
	*num2 = *num1 ^ *num2;
	*num1 = *num1 ^ *num2;
}

int main()
{
	int a = 5;
	int b = 6;

	swap1(&a, &b);
	printf("a == %d, b == %d\n", a, b);
	swap2(&a, &b);
	printf("a == %d, b == %d\n", a, b);
	swap3(&a, &b);
	printf("a == %d, b == %d\n", a, b);

	return 0;
}

运行结果:
在这里插入图片描述
注意:
方法二看上去虽然可以实现不使用临时变量就交换两个数,但实际上,这个代码是存在问题的 - - num1 + num2可能会溢出。
虽然,在VS编译器中,该方法实际执行过程中,在出现溢出时的结果并没有出错,但这并不意味着,该方法正确;在c语言中有符号整数的溢出是未定义行为,因此该代码的“正确性”是依赖于编译器和硬件的,具有偶然性。

交换两个整数应该使用第一种或第三种方法。

练习

求一个整数存储在内存中的二进制中1的个数。

方法一,请思考该方法是否存在问题

#include <stdio.h>

int main()
{
	int num = 7;
	int count = 0;

	int n = num;
	while (n)
	{
		if (n % 2 == 1)
		{
			count++;
		}
		n = n / 2;
	}
	printf("%d中1的个数 == %d\n", num, count);

	return 0;
}

分析
当num为负数时,例如,-1 - - 内存中的数是以补码形式保存 - -> 11111111111111111111111111111111 - - 有32个1
但 -1 % 2 == 1、-1 / 2 == 0,此时count == 1循环结束,结果错误。
显然,该方法无法计算负数的二进制中1的个数。

方法二

#include <stdio.h>

int main()
{
	int num = 7;
	int i = 0;
	int count = 0;

	// int类型内存中占32bit
	for (i = 0; i < 32; i++)
	{
		if ((num >> i) & 1)
		{
			count++;
		}
	}
	printf("%d中1的个数 == %d\n", num, count);

	return 0;
}

分析
该方法能够完成所需功能,但应该注意到,该方法对于所有的数都会进行32次循环,当它处理1时 - - 仅仅只有1个二进制1,但却也需要处理32次,能否进行优化呢?

方法三

#include <stdio.h>

int main()
{
	int num = 7;
	int count = 0;

	int n = num;
	while (n)
	{
		n = n & (n - 1);
		count++;
	}
	printf("%d中1的个数 == %d\n", num, count);

	return 0;
}

分析
该方法的处理次数与待处理数有关,它有多少个1就循环多少次。
举个例子:

7的二进制形式
第一次
0111 - - n
0110 - - n - 1
0110 - - n = n & (n - 1)
第二次
0101 - - n - 1
0100 - - n = n & (n - 1)
第三次
0011 - - n - 1
0000 - - n = n & (n - 1)


n = n & (n - 1)这个运算每次都将n最后一个1化为0。

四、赋值操作符

赋值操作符,能让你给一个变量赋一个你任意想要的值。

int height = 160; // 身高
height = 180; // 不满意就赋值
float score = 3.0f; // 评分
score = 13.0f; // 赋值为13.0

赋值操作符=,可以连续使用。

int x = 1;
int y = 2;
int z = 3;
z = y = x + 2;

// 对比上述代码
y = x + 2;
z = y;

// 下面的代码更让人容易接受,可读性更高。

一些复合赋值操作符:

+=
-=
*=
/=
%=
>>=
<<=
&=
|=
^=

//x += 3 等价于 x = x + 3
// 其他的与之用法相同

五、单目操作符

1.单目操作符的介绍

单目操作符的种类

!  	    // 逻辑反操作
-       // 负值
+       // 正值
&       // 取地址
sizeof  // 操作数的类型长度(以字节为单位)
~       // 按二进制位取反
++      // 前置、后置++
--      // 前置、后置--
*       // 间接访问操作符(解引用操作符) -- 指针的使用
(类型)  // 强制类型转换

// 其中 x++ 等价于 x += 1
// -- 用法类似
// 除此之外,++ 和 -- 还分为前置和后置之分
// x++ 和 --x,虽然都是对x自增1,但x++是用后再加,++x是用前先加
// 举个例子
int x = 3;
int y = 3;
printf("x++ == %d\n", x++);
printf("++y == %d\n", ++y);

// 输出结果为:
// x++ == 3
// ++y == 4
// x是先被输出再+1
// y是先+1再输出

// 使用示例
#include <stdio.h>

int main()
{
	if (!1)
		printf("!1\n");
	else
	{
		printf("0\n");
	}

	int a = 1;
	printf("a == %d, -a == %d, +a == %d\n", a, -a, +a);

	printf("a`s address == %p\n", &a);

	printf("sizeof(a) == %d\n", sizeof(a));

	a = -1;
	printf("~a == %d\n", ~a);

	int x = 3;
	int y = 3;
	printf("x++ == %d\n", x++);
	printf("++y == %d\n", ++y);

	printf("*(&a) == %d\n", *(&a));

	a = 97;
	printf("(char)a == %c\n", (char)a);

	return 0;
}

示例运行结果:
在这里插入图片描述

关于sizeof,它其实是一个操作符,并非是一个函数,验证代码如下

#include <stdio.h>

int main()
{
	int a = 10;
	printf("sizeof int == %d\n", sizeof a);

	return 0;
}

在这里插入图片描述
可以看到,输出确实是4,这里的sizeof并没有使用()函数调用,这也证明了sizeof的确不是函数。

注:sizeof int这是不允许的
在这里插入图片描述

2.sizeof 和数组

看下面的代码,你觉得它们会输出什么?

#include <stdio.h>

void test1(int a[])
{
	printf("sizeof a == %d\n", sizeof(a));
}

void test2(char b[])
{
	printf("sizeof b == %d\n", sizeof(b));
}

int main()
{
	int a[10] = { 3 };
	char b[10] = { 'a' };
	printf("sizeof a == %d\n", sizeof(a)); 
	printf("sizeof b == %d\n", sizeof(b));
	test1(a);
	test2(b);
	int x = 10;
	printf("sizeof x == %d\n", sizeof(x += 2));
	printf("x == %d\n", x);

	return 0;
}

得出你的结果了吗?特别是最后的x的输出是多少?
运行结果如下:
在这里插入图片描述
有没有出乎你的预料?
test1()和test2()中的输出都为4是因为数组名表示的首元素的地址,地址在32位平台中占4个字节,所以输出4
而在main()函数中的输出为40和10是因为数组名的表示含义有两个特例:

  1. sizeof(<数组名>),这里的数组名表示整个数组;
  2. &<数组名>,这里的数组名也表示整个数组;

所以主函数中的输出为整个数组在内存中所占的字节数。


对于最后的x输出的值为4,首先,我们要先明确一个表达式有两种属性:

  1. 值属性
  2. 类型属性

例如:
int a = 10;
a这个表达式它的值属性此时为10;类型属性为int
char b = ‘a’;
b这个表达式它的值属性此时为98 - - 字符在内存中以ASCII码形式存储;类型属性为char
sizeof这个操作符在使用时,仅需要使用表达式的类型属性即可 - - 类型决定了它在内存中所占据的空间,不关心它的值为多少,其中的表达式不会真的计算。
也就是说sizeof(x += 2)中的x+=2并没有计算,仅仅是获取到x是一个int类型即可,所以x的值没有发生改变

你觉得下面的代码会输出什么?

#include <stdio.h>

int main()
{
	char a[] = "hello";
	char b[] = "hello world";

	if (sizeof(a) - sizeof(b) < 0)
	{
		printf("a shorter than b\n");
	}
	else
	{
		printf("a longer than b\n");
	}

	return 0;
}

运行结果:
在这里插入图片描述
这是为什么呢?a明明比b短,为什么会输出a比b更长呢?
让我们来看看sizeof的定义:
在这里插入图片描述
在这里插入图片描述
通过size_t的定义可知,它是无符号整型的别名。所以,sizeof返回的值是个无符号数,两个无符号数的运算恒大于等于0,所以上面的代码一定会输出else中的语句。

六、关系操作符

<
<=
>
>=
==
!=

这些关系操作符用法简单,就不再赘述。只需要注意不要将 = 和 == 混用即可。

七、逻辑操作符

&& // 逻辑与
|| // 逻辑或

// 结果只有真和假

注意区分按位与和逻辑与,按位或和逻辑或

1&2 == 0
1&&2 == 1 // 真

1|2 == 3
1||2 == 1 // 真

一道360的面试题:

#include <stdio.h>

int main()
{
	int i = 0, a = 0, b = 2, c = 3, d = 4;
	i = a++ && ++b && d++;
	//i = a++ || ++b || d++;

	printf("a = %d\nb = %d\nc = %d\nd = %d\n", a, b, c, d);

	return 0;
}

i = a++ && ++b && d++;
这里的输出结果是什么呢?
a = 1, b = 3, c = 3, d = 5?
运行结果:

在这里插入图片描述
i = a++ || ++b || d++;
这里的输出结果是又是什么呢?
a = 1, b = 3, c = 3, d = 5?
在这里插入图片描述


分析
从第一张图中可以看出i = a++ || ++b || d++;这条语句中,仅执行了a++,后续的操作并没有执行,这里因为a++是后置++,在&&操作的过程中是0为假,逻辑与&&中只要有一个操作数为假,则整个语句为假,后续的操作没有执行,所以结果为1 2 3 4;
第二张图中能看出执行了a++和++b,与上面分析类似,逻辑或||中只要有一个操作数为真,则整个语句为真,++b前置++,结果为真,所以,后续的d++就没有执行,最终输出结果为1 3 3 4。

八、条件操作符

exp1 ? exp2 : exp3
使用示例:

int a = 1;
int b = 2;
if(a > b)
{
	printf("%d", a);
}
else
{
	printf("%d", b);
}

// 用条件操作符改写
(a >b) ? printf("%d", a) : printf("%d", b);

九、逗号表达式

exp1, exp2, exp3, …, expN
逗号表达式就是用,隔开的多个表达式。
从左往右依次计算。整个表达式的结果为最后一个表达式的结果。

#include <stdio.h>

int main()
{
	// 代码1
	int a = 1;
	int b = 2;
	int c = (a > b, a = b + 10, a, b = a + 1);
	printf("c == %d\n", c);
	// c是多少?
	
	// 代码2
	int d = -1;
	if (a = b + 1, c = a / 2, d > 0)
	{
		printf("if\n");
	}
	else
	{
		printf("else\n");
	}
	// 输出什么?

	// 代码3
	a = get_val();
	count_val(a);
	while (a > 0)
	{
		// 业务代码
		get_val();
		count_val(a);
	}
	// 上面代码能改成
	while (a = get_val(), count_val(a), a > 0)
	{
		// 业务代码
	}

	return 0;
}
  1. 代码1:此代码是用逗号表达式对c进行赋值操作,将逗号表达式从左到右依次执行,且此逗号表达式的结果为最后一个表达式b = a + 1,此时b == 13,所以c被赋值为13
  2. d > 0这个关系表达式的值为整个逗号表达式的值,d == -3,d > 0 为假,所以输出else

在这里插入图片描述

十、下标引用、函数调用和结构成员

下标引用操作符[]有两个操作数 - - 一个数组名 + 一个索引值,
例如:

int a[10] = { 0 };
a[0] = 1; // 使用下标引用操作符
// a 和 0 是操作数
// 实际上等价于 *(a + 0) -- 指针的使用,后续章节会详细介绍

函数调用() - - 接受一个或多个操作数:第一个是函数名,剩余的操作数就是传递给函数的参数
例如:

#include <stdio.h>

void test1()
{
	printf("test1\n");
}

void test2(const char* str)
{
	printf("test2:");
	printf("%s\n", str);
}

int main()
{
	test1(); // 使用了()操作符,只有一个操作数 -- 函数名test1
	test2("This is test2"); // 使用了()操作符,有两个操作数 -- 函数名test1,字符串参数

	return 0;
}

结构体成员访问操作符. 和-> - - 都有两个操作数
. - - 使用示例:结构体.成员名
-> - - 使用示例:结构体指针->成员名
例如:

#include <stdio.h>

typedef struct
{
	int age;
	char name[20];
	char sex[5];
} Stu;

int main()
{
	Stu stu = { 30, { 0 }, { 0 } };
	Stu* pstu = &stu;

	// .操作符
	stu.age = 18;
	printf("age == %d\n", stu.age);


	// ->操作符
	pstu->age = 20;
	printf("age == %d\n", stu.age);

	return 0;
}

运行结果:
在这里插入图片描述

十一、表达式求值

表达式的求值的顺序一部分是由操作符的优先级和结合性来决定的。
同样,有些表达式的操作数在求值过程中可能需要转换为其他类型。

1.隐式类型转换

c的整型算术运算总是至少以缺省的整数类型的精度来进行的 - - 求值数在内存中的基础长度为4字节,短了需要补足。
为了获得这个精度,表达式中的字符和短整型操作数要在使用之前转换为普通整型,这种转换称为整型提升

整型提升的意义:
表达式的整型运算要在CPU的相应运算器件中执行,CPU内整型运算器(ALU)的操作数的字节长度一般就是int的字节长度,同时也是CPU的通用寄存器的长度。
因此,即使是两个char类型的数相加,在CPU中执行的时,也需要先将其转换为CPU内的整型操作数的标准长度。
通用CPU(general-purpose CPU)是难以实现8bit直接相加的运算的。所以,表达式中各种长度可能小于int长度的整型值,都必须先转换为int或unsigned int,然后才能送往CPU去执行运算。
例如:

char a, b, c;
...
a = b + c;
// b 和 c 的值被提升为普通整型,然后执行加法运算,加法运算完成后,对结果进行截断,然后再存储到a中。

如何进行整型提升呢?

整型提升是基于变量的数据类型的符号位来提升的

// 负数的整型提升
char a = -1;
// 变量a的二进制位(补码)只有8个bit位:
// 11111111
// char是有符号的 char
// a 的符号位为1,在整型提升时,高位补充符号位,即1
// 提升后的结果为:
// 11111111111111111111111111111111 

// 正数的整型提升
char b = 1;
// 变量b的二进制位(补码)只有8个bit位:
// 00000001
// char是有符号的 char
// a 的符号位为0,在整型提升时,高位补充符号位,即0
// 提升后的结果为:
// 00000000000000000000000000000001

// 如果是无符号数的整型提升,高位直接补 0

整型实例1:下面代码会输出什么?

#include <stdio.h>

int main()
{
	char a = 0x80;
	short b = 0x8000;
	int c = 0x800000;

	if (a == 0x80)
	{
		printf("a\n");
	}
	if (b == 0x8000)
	{
		printf("b\n");
	}
	if (c == 0x800000)
	{
		printf("c\n");
	}

	return 0;
}

运行结果:
在这里插入图片描述
可以看到仅输出了一个c,说明在判断中a != 0x80,b != 0x8000,
这是因为,在进行a == 0x80和b == 0x8000时,a和b的类型长度小于4,需要进行整型提升,且它们的符号位为1,提升是补1,实际上a == 0x80是11111111111111111111111110000000 == 00000000000000000000000010000000为假,b == 0x8000是11111111111111111000000000000000 == 00000000000000001000000000000000为假,只有c == 0x800000为真。

整型实例2:下面代码会输出什么?

#include <stdio.h>

int main()
{
	char c = 1;
	printf("c == %u\n", sizeof(c));
	printf("-c == %u\n", sizeof(-c));
	printf("+c == %u\n", sizeof(+c));

	return 0;
}

运行结果:
在这里插入图片描述
c只要参与表达式运算,就需要整型提升,表达式+c和-c都参与了表达式运算,就都发生了提升,所以输出为4 - - int类型的大小,sizeof©中的c没有进行整型提升,所以输出1。

2.算术转换

如果某个操作符的各个操作数属于不同的类型,那么除非其中一个操作数转换为另一个操作数的类型,否则操作就无法进行。下面的层次体系称为寻常算术转换。

long double
double
float
unsigned long int 
long int 
unsigned int
int

如果某个操作数的类型在上面这个列表中排名较低,那么首先要转换为另外一个操作数的类型后再执行运算。
算术转换要合理,否则会有一些潜在的问题

float f = 3.14f;
int num = f; // 隐式转换,会有精度丢失。

3.操作符属性

复杂表达式的求值有三个影响因素:

  1. 操作符的优先级
  2. 操作符的结合性
  3. 是否控制求值顺序

两个相邻的操作符先执行哪个?取决于它们的优先级,优先级相同,则取决于它们的结合性。

操作符优先级描述用法示例结合性是否控制求值顺序
()1聚组(表达式)与表达式相同N/A
()1函数调用函数名(参数, …, 参数)L-R否
[]1下标引用数组名[下标]L-R否
.1访问结构成员结构体.成员名L-R否
->1访问结构指针成员结构体指针->成员名L_R否
++2后置自增表达式++L-R否
--2后置自减表达式- -L-R否
!2逻辑反!表达式R-L否
~2按位取反~表达式R-L否
+2单目,表示正值+表达式R-L否
-2单目,表示负值-表达式R-L否
++2前置自增++表达式R-L否
--2前置自减- -表达式R-L否
*2间接访问*指针变量R-L否
&2取地址&变量名R-L否
sizeof2取其长度,以字节为单位sizeof(表达式)R-L否
(类型)2强制类型转换(类型)表达式R-L否
*3乘法表达式 * 表达式L-R否
/3除法表达式 / 表达式L-R否
%3整数取余表达式 % 表达式L-R否
+4加法表达式 + 表达式L-R否
-4减法表达式 - 表达式L-R否
<<5左移变量 << 表达式L-R否
>>5右移变量 >> 表达式L-R否
>6大于表达式 > 表达式L-R否
>=6大于等于表达式 >= 表达式L-R否
<6小于表达式 < 表达式L-R否
<=6小于等于表达式 <= 表达式L-R否
==7等于表达式 == 表达式L-R否
!=7不等于表达式 != 表达式L-R否
&8按位与表达式 & 表达式L-R否
|9按位或表达式 | 表达式L-R否
^10按位异或表达式 ^ 表达式L-R否
&&11逻辑与表达式 && 表达式L-R是
||12逻辑或表达式 || 表达式L-R是
?:13条件操作符表达式 ? 表达式 : 表达式N/A是
=14赋值变量 = 表达式R-L否
+=14加后赋值变量 += 表达式R-L否
-=14减后赋值变量 -= 表达式R-L否
*=14乘后赋值变量 *= 表达式R-L否
/=14除后赋值变量 /= 表达式R-L否
%=14取模后赋值变量 %= 表达式R-L否
<<=14左移后赋值变量 <<= 表达式R-L否
>>=14右移后赋值变量 >>= 表达式R-L否
&=14按位与后赋值变量 &= 表达式R-L否
|=14按位或后赋值变量 |= 表达式R-L否
^=14按位异或后赋值变量 ^= 表达式R-L否
,15逗号表达式表达式, 表达式, …, 表达式R-L否

一些问题表达式

表达式1

// 表达式的求值部分由操作符的优先级决定
// 表达式1
a * b + c * d + e * f
// * 的优先级比 + 高,只能保证第一个 * 比 + 更早运算,
// 但无法保证e * f 比a * b + c * d 更早运算。
// 即可能出现
// a. 
// (1).(a * b) 
// (2).(c * d) 
// (3).(e * f)
// (4).(1) + (2)
// (5).(4) + (3)
// b. 
// (1).(a * b) 
// (2).(c * d) 
// (3).(1) + (2)
// (4).(e * f)
// (5).(3) + (4)

表达式2

// 表达式2
c + -- c;
// 同样的从优先级中只能获知,--比 + 更早运算,
// 但左边的c是在--c之前获取的,还是在其之后获取的这并不确定,该代码同样存在歧义。
// 假设 c = 2
// 上述代码可能为:
// 1. 2 + --2; // --c执行之前,前面的c就获取值
// 2. 1 + --2; // --c执行之后,前面的c才获取值

表达式3

// 表达式3
#include <stdio.h>

int main()
{
	int i = 10;
	i = i-- - --i * (i = -3) * i++ + ++i;
	printf("i == %d\n", i);

	return 0;
}

这是一个很典型的错误代码,在不同的编译器中的结果都不相同,如果,那个程序员写了这样的代码,一定会被人骂"猪队友"。

表达式4

// 代码4
#include <stdio.h>

int func()
{
	static int count = 1;
	return count++;
}

int main()
{
	int answer;
	answer = func() - func() * func();
	printf("answer == %d\n", answer);

	return 0;
}

这个代码有问题吗?
有问题!
虽然表达式中的 - 和 * 可以通过优先级来决定谁先计算,但func()的计算顺序是依赖于编译器的,有可能先计算第一个func(),再计算第二个func(),最后计算第三个func(),这样的结果为 1 - 2 * 3;
也可能先计算第二个和第三个,最后计算第一个,这两种的计算结果是不同。
所以这样的代码也是不可靠的。

表达式5

#include <stdio.h>

int main()
{
	int i = 1;
	int ret = 0;
	ret = (++i) + (++i) + (++i);
	printf("ret == %d\n", ret);
	printf("i == %d\n", i);

	return 0;
}

这个代码在不同的编译器中的执行结果也是不同的,无法确认表达式中的计算顺序,
比如,上述代码可能出现以下几种计算顺序:

  1. i先自增3次,此时i == 4,之后再进行 + 操作,这样得出的结果为12
  2. 先执行前两个++i,此时i == 3,再执行 + ,表达式转化为 6 + (++i),此时再次执行++i,i == 4,最后执行6 + 4,这样得到的结果为10。

总结
我们写出的表达式的执行顺序,无法通过操作符的属性来唯一确认的话,那么这个表达式就是个问题表达式。


总结

本文对c语言中会使用到的操作符进行较为详细的介绍说明,特别是对移位操作符中的右移操作的两种情况进行了分析,详细说明了sizeof的使用以及使用时可能遇到的问题,逻辑操作符运算过程中可能出现的反直觉问题,表达式求值中整型提升、算术转换、操作符的优先级,并列出、分析了一些问题表达式。

抖音用户评论和ID的采集方法 随着5G时代的普及,各大运营商的套餐纷纷推出,使得流量成本进一步降低。同时5G线路高宽带、低延迟的技术特性和手机普及,让视频用户访问量快速增加。 由于大量的视频工具和拍摄APP的提供,让短视频的拍摄、制作包括发行都变得更加简单便捷,越来越多的普通人可以投入视频创作,创造了更丰富的互联网内容,视频作者从视频推广中能获取粉丝和推广收益。 目前抖音就是目前最重要的短视频平台之一,要及时获取上面的用户评论和ID,需要结合爬虫代理实现采集方案,下面的代码就能快速采集相关信息: package htmlunit; i 阅读详情

相关推荐

Jitsi中的Prosody(谐音:破扫敌)介绍

凭借其灵活的插件系统、强大的安全性和出色的性能,Prosody 成为许多企业和开发者构建定制化通信解决方案的首选。在 Jitsi 的多方视频会议场景中,Prosody 不仅处理基本的信令任务,还需要与其他组件(如 Jitsi Videobridge)协作,确保视频流的正确分发和控制。Prosody 提供了丰富的插件系统,允许开发者通过模块扩展其功能。Prosody 就是一个实现 XMPP 协议的服务器,它支持 XMPP 协议的标准功能,如消息传递、群组聊天、传送状态信息等,并提供了高效、可扩展的服务。

江左散人的专栏 1166

C语言修炼秘籍之——操作符

内些个操作符到底怎么个事儿啊?二进制及其及其进制转换是个什么玩意儿啊?原反补我到现在都还妹学明白你能给我教会唠?这位客官,咱里边儿请,且听我娓娓道来~

2301_80029060的博客 1532

面积二次矩second moment of area 极惯性矩polar moment of area 转动惯量moment of inertia面积一次矩first moment of area

看论文记录一下力学的名词专业术语,后面可以查看。

subtitle_的博客 5367

C语言修炼秘籍【第零章】c语言概述

c语言是一门编程语言。语言的作用就是交流,人们可以利用它与计算机进行交流,让计算机知道你需要它完成什么工作。本系列文章是对笔者在c语言学习的总结记录。本文会先粗略的介绍c语言中需要学习的内容。

PLKDTHFS的博客 1664

Unity Shader学习笔记/Urp/水墨风效果

Unity Shader学习

qq_63133691的博客 2117

Python 爬虫实战:爬取 BOSS 直聘职位,实现岗位要求关键词提取

摘要:本文基于BOSS直聘数据分析师岗位信息,采用Python爬虫技术获取职位数据,通过文本分析和可视化方法研究岗位技能需求特征。研究首先设计爬虫程序获取职位基本信息和岗位要求文本,经数据清洗后运用jieba分词和词频统计提取关键词。结果显示SQL、Python为最核心技能,不同级别岗位需求差异明显:初级重基础数据处理,高级需建模和管理能力;行业间亦存在差异,互联网关注用户增长,金融侧重风控。研究为求职者技能提升提供了数据支持,同时展示了从非结构化文本中挖掘有价值信息的完整流程。

2503_91057718的博客 1765

c语言修炼秘籍【第五章】指针

本文会从介绍指针是什么开始,循序渐进地介绍指针的使用、指针使用时会遇到的问题、指针能进行哪些运算、指针与数组之间的关系,以及指针数组这些内容。本文对c语言中使用的指针进行了较简略的介绍,包括什么是指针,指针类型有什么用,指针的一些运算,什么是野指针,如何利用指针访问数组,二级指针使用中会出现的问题,以及指针数组。

PLKDTHFS的博客 1763

《民机适航入门指南:从法规学习到项目实践的全面解析》

适航(Airworthiness)是指航空器、其发动机、螺旋桨和其他部件在规定条件下,能够安全飞行并满足有关航空法规规定的条件。简而言之,适航代表了航空器在任何时候都能安全运营的状态。适航在民用航空领域尤为关键,因为飞机必须经过严格的适航认证才能投入商用。这些认证的目标是确保飞机能够在广泛的运行条件下保持飞行安全,从而保护乘客、机组和地面人员的生命财产安全。适航法规覆盖了飞机的整个生命周期,包括设计、制造、测试、运营、维护和报废。

qq_41854911的博客 992

c语言修炼秘籍【第二章】函数

本文会从函数是什么开始,逐一介绍函数的语法结构,如何声明和定义一个函数,函数的参数类型,调用函数时的传参类型,函数的调用方式,函数递归,并在最后对汉诺塔、青蛙跳台阶和八皇后问题进行详细分析(包含全部代码)

PLKDTHFS的博客 1443

【FPGA教程案例89】编译码2——使用vivado核实现RS信道编译码

RS编码,又称里所码,即Reed-solomon codes,是一种前向纠错的信道编码,对由校正过采样数据所产生的多项式有效。当接收器正确的收到足够的点后,它就可以恢复原来的多项式,即使接收到的多项式上有很多点被噪声干扰失真。

FPGA/MATLAB学习教程/源码/项目合作开发 3540

C语言使用开源库libusb进行USB设备操作

https://libusb.info/libusb is a C library that provides generic access to USB devices. It is intended to be used by developers to facilitate the production of applications that communicate with USB hardware. It is portable: Using a single cross-platform AP

m0_60352504的博客 1554

c语言修炼秘籍【第六章】结构体

将结构体与数组进行对比:数组是一组相同类型的元素集合;结构体是一些值的集合,它们类型可以不同,这些值被称为成员变量。使用结构体类型定义变量int x;int y;} p1;// 声明类型的同时定义变量p1// 定义结构体变量 p2// 初始化:定义变量的同时赋初值struct Stu// 名字int age;// 年龄int data;// 结构体嵌套的初始化// 结构体嵌套的初始化本文对c语言中使用到的结构体进行了简单介绍,更详细的会在后续攻略中详解。

PLKDTHFS的博客 761

TMC2240 芯片数据手册解读|第三篇 TMC2240 芯片引脚配置与说明

本文详细解析了TMC2240芯片的两款封装(TQFN32和TSSOP38-EP)的引脚分布、电气属性及硬件设计要点。内容涵盖:1)芯片内部功能架构,包括供电、运动控制等五大链路;2)两款封装的核心规范与适用场景;3)七大引脚分类详解(电源、控制、编码器等)及接线要求;4)PCB设计避坑指南,强调电源分割、电荷泵布局等关键点。文章提供完整的原理图绘制和硬件调试指导,适合不同基础的工程师参考实践。

BackCatK Chen的博客 1090

c语言修炼秘籍【第三章】数组

本文将会介绍常用到的一维数组和二维数组是如何使用及它们在内存中是如何存储,说明何为数组越界,并讨论数组在作为函数参数时应该如何使用。在最后附带三子棋和扫雷的代码实现详解。本文对一维和二维数组的初始化、创建、使用,作出的简要的介绍,并通过冒泡排序算法的实现,详细说明了当数组作为函数参数时的性质 - - 传递的是首元素地址。在最后,给出了两个数组的使用实例,附带了完整的实现代码,并配有详细注释。

PLKDTHFS的博客 1257

WINCC7.3破解文件

WINCC7.3硬件够破解文件,有需要的下载!WINCC7.3硬件够破解文件,有需要的下载!

深度学习工具包 DeepLearningToolbox

深度学习工具包Deprecation notice.-----This toolbox is outdated and no longer maintained.There are much better tools available for deep learning than this toolbox, e.g. [Theano](http://deeplearning.net/software/theano/), [torch](http://torch.ch/) or [tensorflow](http://www.tensorflow.org/)I would suggest you use one of the tools mentioned above rather than use this toolbox.Best, Rasmus.DeepLearnToolbox================A Matlab toolbox for Deep Learning.Deep Learning is a new subfield of machine learning that focuses on learning deep hierarchical models of data.It is inspired by the human brain's apparent deep (layered, hierarchical) architecture.A good overview of the theory of Deep Learning theory is[Learning Deep Architectures for AI](http://www.iro.umontreal.ca/~bengioy/papers/ftml_book.pdf)For a more informal introduction, see the following videos by Geoffrey Hinton and Andrew Ng.* [The Next Generation of Neural Networks](http://www.youtube.com/watch?v=AyzOUbkUf3M) (Hinton, 2007)* [Recent Developments in Deep Learning](http://www.youtube.com/watch?v=VdIURAu1-aU) (Hinton, 2010)* [Unsupervised Feature Learning and Deep Learning](http://www.youtube.com/watch?v=ZmNOAtZIgIk) (Ng, 2011)If you use this toolbox in your research please cite [Prediction as a candidate for learning deep hierarchical models of data](http://www2.imm.dtu.dk/pubdb/views/publication_details.php?id=6284)```@MASTERSTHESIS\{IMM2012-06284, author = "R. B. Palm", title = "Prediction as a candidate for learning deep hierarchical models of data", year = "2012",}```Contact: rasmusbergpalm at gmail dot comDirectories included in the toolbox-----------------------------------`NN/` - A library for Feedforward Backpropagation Neural Networks`CNN/` - A library for Convolutional Neural Networks`DBN/` - A library for Deep Belief Networks`SAE/` - A library for Stacked Auto-Encoders`CAE/` - A library for Convolutional Auto-Encoders`util/` - Utility functions used by the libraries`data/` - Data used by the examples`tests/` - unit tests to verify toolbox is workingFor references on each library check REFS.mdSetup-----1. Download.2. addpath(genpath('DeepLearnToolbox'));Example: Deep Belief Network---------------------```matlabfunction test_example_DBNload mnist_uint8;train_x = double(train_x) / 255;test_x = double(test_x) / 255;train_y = double(train_y);test_y = double(test_y);%% ex1 train a 100 hidden unit RBM and visualize its weightsrand('state',0)dbn.sizes = [100];opts.numepochs = 1;opts.batchsize = 100;opts.momentum = 0;opts.alpha = 1;dbn = dbnsetup(dbn, train_x, opts);dbn = dbntrain(dbn, train_x, opts);figure; visualize(dbn.rbm{1}.W'); % Visualize the RBM weights%% ex2 train a 100-100 hidden unit DBN and use its weights to initialize a NNrand('state',0)%train dbndbn.sizes = [100 100];opts.numepochs = 1;opts.batchsize = 100;opts.momentum = 0;opts.alpha = 1;dbn = dbnsetup(dbn, train_x, opts);dbn = dbntrain(dbn, train_x, opts);%unfold dbn to nnnn = dbnunfoldtonn(dbn, 10);nn.activation_function = 'sigm';%train nnopts.numepochs = 1;opts.batchsize = 100;nn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.10, 'Too big error');```Example: Stacked Auto-Encoders---------------------```matlabfunction test_example_SAEload mnist_uint8;train_x = double(train_x)/255;test_x = double(test_x)/255;train_y = double(train_y);test_y = double(test_y);%% ex1 train a 100 hidden unit SDAE and use it to initialize a FFNN% Setup and train a stacked denoising autoencoder (SDAE)rand('state',0)sae = saesetup([784 100]);sae.ae{1}.activation_function = 'sigm';sae.ae{1}.learningRate = 1;sae.ae{1}.inputZeroMaskedFraction = 0.5;opts.numepochs = 1;opts.batchsize = 100;sae = saetrain(sae, train_x, opts);visualize(sae.ae{1}.W{1}(:,2:end)')% Use the SDAE to initialize a FFNNnn = nnsetup([784 100 10]);nn.activation_function = 'sigm';nn.learningRate = 1;nn.W{1} = sae.ae{1}.W{1};% Train the FFNNopts.numepochs = 1;opts.batchsize = 100;nn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.16, 'Too big error');```Example: Convolutional Neural Nets---------------------```matlabfunction test_example_CNNload mnist_uint8;train_x = double(reshape(train_x',28,28,60000))/255;test_x = double(reshape(test_x',28,28,10000))/255;train_y = double(train_y');test_y = double(test_y');%% ex1 Train a 6c-2s-12c-2s Convolutional neural network %will run 1 epoch in about 200 second and get around 11% error. %With 100 epochs you'll get around 1.2% errorrand('state',0)cnn.layers = { struct('type', 'i') %input layer struct('type', 'c', 'outputmaps', 6, 'kernelsize', 5) %convolution layer struct('type', 's', 'scale', 2) %sub sampling layer struct('type', 'c', 'outputmaps', 12, 'kernelsize', 5) %convolution layer struct('type', 's', 'scale', 2) %subsampling layer};cnn = cnnsetup(cnn, train_x, train_y);opts.alpha = 1;opts.batchsize = 50;opts.numepochs = 1;cnn = cnntrain(cnn, train_x, train_y, opts);[er, bad] = cnntest(cnn, test_x, test_y);%plot mean squared errorfigure; plot(cnn.rL);assert(er<0.12, 'Too big error');```Example: Neural Networks---------------------```matlabfunction test_example_NNload mnist_uint8;train_x = double(train_x) / 255;test_x = double(test_x) / 255;train_y = double(train_y);test_y = double(test_y);% normalize[train_x, mu, sigma] = zscore(train_x);test_x = normalize(test_x, mu, sigma);%% ex1 vanilla neural netrand('state',0)nn = nnsetup([784 100 10]);opts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samples[nn, L] = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.08, 'Too big error');%% ex2 neural net with L2 weight decayrand('state',0)nn = nnsetup([784 100 10]);nn.weightPenaltyL2 = 1e-4; % L2 weight decayopts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samplesnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex3 neural net with dropoutrand('state',0)nn = nnsetup([784 100 10]);nn.dropoutFraction = 0.5; % Dropout fraction opts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samplesnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex4 neural net with sigmoid activation functionrand('state',0)nn = nnsetup([784 100 10]);nn.activation_function = 'sigm'; % Sigmoid activation functionnn.learningRate = 1; % Sigm require a lower learning rateopts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samplesnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex5 plotting functionalityrand('state',0)nn = nnsetup([784 20 10]);opts.numepochs = 5; % Number of full sweeps through datann.output = 'softmax'; % use softmax outputopts.batchsize = 1000; % Take a mean gradient step over this many samplesopts.plot = 1; % enable plottingnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex6 neural net with sigmoid activation and plotting of validation and training error% split training data into training and validation datavx = train_x(1:10000,:);tx = train_x(10001:end,:);vy = train_y(1:10000,:);ty = train_y(10001:end,:);rand('state',0)nn = nnsetup([784 20 10]); nn.output = 'softmax'; % use softmax outputopts.numepochs = 5; % Number of full sweeps through dataopts.batchsize = 1000; % Take a mean gradient step over this many samplesopts.plot = 1; % enable plottingnn = nntrain(nn, tx, ty, opts, vx, vy); % nntrain takes validation set as last two arguments (optionally)[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');```[![Bitdeli Badge](https://d2weczhvl823v0.cloudfront.net/rasmusbergpalm/deeplearntoolbox/trend.png)](https://bitdeli.com/free "Bitdeli Badge")

c语言修炼秘籍【第一章】分支与循环语句

在处理现实中的问题时,我们总会遇到需要作出选择或是需要重复多次的完成一种事件。你去图书馆学习,需要日复一日的坚持,每多学一天,你的代码功力就能+1,当你的代码功力,达到了某一程度,你就能够迎娶白富美,走上人生巅峰。为了实现这些目的,c语言提供了分支语句,用以实现选择;提供循环语句,用以实现重复多次的机械活动;表达式语句函数调用语句控制语句复合语句空语句分支、循环语句属于控制语句。控制语句用于控制程序的执行流程,以实现程序的各种结构方式,它们由各种特定的语句定义符构成;

PLKDTHFS的博客 1244

假人有限元模型

假人模型,车辆碰撞假人有限元模型,适合做乘员安全的研究者利用

c语言修炼秘籍【第七章】const与c语言中一些错误代码

而数组内部的内存使用分配是从低到高的,arr[0]的地址在最低位,当循环进行到第11次 - - arr[11]设置的其实是i的值,此时将 i 的值设为了0,循环继续。这是因为,程序要进行报错也是需要执行时间的,访问arr[11]时程序仍处于循环中,没有时间报错就又进入了下一次循环。test1()可以正常使用指针对n为进行修改,也可以对p指向的位置进行修改。test2()不能通过指针对n进行修改,可以对p指向的位置进行修改。,arr后分配,地址会比i的地址低,分配到的地址为。,i先分配空间,地址为。

PLKDTHFS的博客 674

jdk-8u144-linux-x64.tar.gz.

该资源为搭建Linux系统Java环境用,资源的后缀为tar.gz格式的压缩包

c语言修炼秘籍 - - 禁(进)忌(阶)秘(技)术(巧)【第一式】数据的存储

本章节的重点是数据类型详细介绍整型在内存中的的存储:原码、反码、补码大小端字节序介绍及判断浮点型在内存中的存储解析char // 字符数据类型short // 短整型int // 整型long // 长整型long long // 更长的整型float // 单精度浮点数double // 双精度浮点数// c语言有没有字符串类型?// 没有以及他们所占存储空间的大小。使用这个类型开辟内存空间的大小(大小决定了使用范围)如何看待内存空间的视角什么是大端小端?

PLKDTHFS的博客 1487

c语言修炼秘籍 - - 禁(进)忌(阶)秘(技)术(巧)【第二式】指针

字符指针数组指针指针数组数组传参和指针传参几种数组、指针之间的比较函数指针函数指针数组指向函数指针数组的指针回调函数指针和数组的笔试题指针是一个存储地址的变量,地址是一块内存空间的唯一标识;指针的大小是固定的(4字节或8字节);指针是有类型的,它的类型决定它 + - 整数时移动的步长、*解引用操作时的权限;指针是能够进行运算的,+ - 整数、指针相减、指针的关系运算;接下来,就让我们继续探讨指针的进阶主题。对于普通的指针,我们都已经很熟悉了。

PLKDTHFS的博客 1518

c语言修炼秘籍 - - 禁(进)忌(阶)秘(技)术(巧)【第七式】程序的编译

程序的翻译环境程序的执行环境c语言程序的编译+链接预定义符号介绍预处理指令#define宏和函数的对比预处理操作符#和##的介绍命令定义预处理指令#include预处理指令#undef条件编译__FILE__ // 进行编译的源文件__LINE__ // 文件当前行号__DATE__ // 文件被编译的日期__TIME__ // 文件被编译的时间__STDC__ // 如果编译器遵循ANSI C,其值为1,否则未定义。

PLKDTHFS的博客 1195

c语言修炼秘籍 - - 禁(进)忌(阶)秘(技)术(巧)【第五式】动态内存管理

大家在使用数组时肯定都会有过这样的疑问,如果一个数组的长度不足了,可以延长这个数组吗?使其能够满足存储的需求。又或者是现在不确定数组应该设为多长,先把它放着,等之后再设置它的长度。上面的这些需求,数组显然是无法实现的,那么c语言中有什么东西能够做到这一点呢?动态内存分配!你现在不知道需要多大的空间,没关系,之后你明确了的时候,使用动态内存分配,将你要的空间分配给即可。为什么存在动态内存分配动态内存函数的介绍mallocfreecallocrealloc常见的动态内存错误。

PLKDTHFS的博客 1293

c语言修炼秘籍 - - 禁(进)忌(阶)秘(技)术(巧)【第六式】文件操作

为什么使用文件什么是文件文件的打开和关闭文件的顺序读写文件的随机读写文本文件和二进制文件文件读取结束的判定文件缓冲区磁盘上的文件就是文件。但是在程序设计上,我们谈到的文件一般有两种:程序文件、数据文件;本节详细的对c语言中数据文件的使用进行了详细的介绍,包括对文件读写操作使用的函数,以及通过文件位置指针的操作函数来对文件进行随机读写,并指出了feof函数可能出现的使用错误,在最后,介绍了文件缓冲区的概念;

PLKDTHFS的博客 1226

c语言修炼秘籍 - - 禁(进)忌(阶)秘(技)术(巧)【第三式】字符函数和字符串函数

c语言中对字符和字符串的处理非常频繁,但c语言本身是没有字符串类型的,字符串通常存储在常量字符串或字符数组中。常量字符串适用于不需要对其进行修改的字符串函数。strlen函数的作用是获得一个字符串的长度;接收一个const修饰的字符指针变量,这个指针指向的字符串必须有结束符\0;返回一个无符号(易错点)的int类型的数值,表示这个字符串的长度(不包括结束符\0如果没有返回值,则表示出错;// 使用示例int main()printf("a的长度 = %d\n", strlen(a));

PLKDTHFS的博客 926

JAVA修炼秘籍第二章《逐渐魔化》

JAVA修炼秘籍第一章《痛苦的折磨》:https://blog.youkuaiyun.com/Coach_zhang/article/details/119217583?spm=1001.2014.3001.5501 第二章目录1 — 数据类型细节部分byte类型short,int,long类型float,double类型boolean类型final 常量关键字操作符介绍+,-,*,%,\符合运算符++,- -逻辑运算符位运算符移位运算 1 — 数据类型细节部分 上一章节中,我们说到数据类型的介绍,这一章节我们先把

Coach_zhang的博客 606

【c++修炼秘籍】C/C++内存管理

c/c++的内存管理相似但也不同,本文简要分析c/c++的内存管理方式,主要会使用new和delete进行c++的动态内存管理

m0_62824408的博客 322

重生之我在蓝星用C修仙直到成就仙王

不知道过去了多久蓝星的灵气早已枯竭,后来的人们开发出了新的修炼流派,用一行行的代码来进行修炼,我遇见了一个大成修士他说他叫鹏哥,我也顺利成为了他的弟子,当我第一次看到新的修炼秘籍时我便被上面的技巧深深吸引,无论是操作符还是数组亦或是算法与数据结构都让我深深着迷,太精妙了,我惊呼道,这秘籍里面的一切都那么深奥与精妙绝伦,我发誓要好好修炼直到我成为最厉害的那一个。从今天开始我将开始我的C语言修仙征途,诸位道友请不吝赐教。

soyoung246的博客 145

c语言修炼秘籍 - - 禁(进)忌(阶)秘(技)术(巧)【第四式】自定义类型详解(结构体、枚举、联合)

c语言中虽然定义得有许多的类型,如char、int、double等等,但是现实生活中需要描述的问题,仅仅使用这些已有类型是不够的,所以在c语言中,程序员可以根据自己的需要,定义出自己的类型结构,以满足需求。结构体:结构体类型的声明结构的自引用结构体变量的定义和初始化结构体内存对齐结构体传参结构体实现位段(位段的填充&可移植性)枚举枚举类型的定义枚举的优点枚举的使用联合联合类型的定义联合的特点联合的使用有了结构体类型,之后应该如何定义变量呢?

PLKDTHFS的博客 989
上一篇: c语言修炼秘籍【第三章】数组
下一篇: c语言修炼秘籍【第五章】指针
YKPG
博客等级 码龄6年 344粉丝 37原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值