trinity C Programming Slide 1 重要知识点
1. Constant Suffixes (常量后缀)
- Understanding Constant Suffixes
英文:
In C, numeric constants can have suffixes that specify their type, which can affect how calculations are performed. Common suffixes include:
Lorl: Indicates a long integer.Uoru: Indicates an unsigned integer.Forf: Indicates a float.Dord: Indicates a double.
中文:
在 C 语言中,数值常量可以具有后缀,以指定其类型,这会影响计算的执行方式。常见的后缀包括:
L或l:表示长整型。U或u:表示无符号整型。F或f:表示浮点数。D或d:表示双精度浮点数。
- Example of Overflow Without Suffix
英文:
long int ans = 2000000000 * 2;
- Explanation: In this line,
2000000000is treated as anintby default because it has no suffix. The maximum value for a 32-bit signed integer is2147483647. When you multiply2000000000by2, the result (4000000000) exceeds the maximum value for a signedint, causing an overflow. This results in a negative value due to how integer overflow wraps around in binary representation. - Actual Result: The value of
ansbecomes-294967296, which is incorrect.
中文:
long int ans = 2000000000 * 2;
- 解释:在这一行中,
2000000000默认被视为int类型,因为它没有后缀。32 位带符号整型的最大值为2147483647。当你将2000000000乘以2时,结果(4000000000)超过了带符号int的最大值,导致 溢出。这会导致一个负值,因为整型溢出在二进制表示中是如何环绕的。 - 实际结果:
ans的值变为-294967296,这是不正确的。
- Using the Long Suffix
英文:
long int ans = 2000000000L * 2;
- Explanation: Here, the
Lsuffix indicates that2000000000is a long integer. As a result, the multiplication operation is performed using thelongtype instead of the defaultinttype. The maximum value for a 32-bitlongis much larger than that of anint, so multiplying2000000000Lby2(which results in4000000000) does not cause an overflow. - Actual Result: The value of
ansis now correctly set to4000000000.
中文:
long int ans = 2000000000L * 2;
- 解释:在这里,
L后缀表示2000000000是一个长整型。因此,乘法操作是使用long类型而不是默认的int类型进行的。32 位long的最大值远大于int的最大值,因此将2000000000L乘以2(结果为4000000000)不会导致溢出。 - 实际结果:
ans的值现在正确地设置为4000000000。
- Key Takeaways
英文:
- Constant Type Matters: When performing arithmetic operations, the types of constants involved determine the resulting type and can lead to overflow if not correctly specified.
- Suffix Usage: Always use appropriate suffixes for constants to avoid unintended behavior, especially when dealing with large numbers or performing arithmetic that can exceed the limits of the default data types.
- Preventing Overflows: By specifying the type of a constant, you can ensure that calculations are performed in a data type that can handle the expected range of values, thus avoiding overflow issues.
中文:
- 常量类型重要:在执行算术操作时,涉及的常量类型决定了结果类型,如果未正确指定,可能会导致溢出。
- 后缀使用:始终对常量使用适当的后缀,以避免意外行为,特别是在处理大数或执行可能超出默认数据类型限制的算术运算时。
- 防止溢出:通过指定常量的类型,可以确保计算在能够处理预期值范围的数据类型中进行,从而避免溢出问题。
- Best Practices
英文:
- When dealing with large constants or calculations, it’s a good practice to explicitly specify the type using suffixes.
- Use the largest appropriate data type for calculations to avoid potential overflows. For instance, using
long long intfor very large numbers if necessary.
中文:
- 在处理大常量或计算时,明确使用后缀指定类型是一个好习惯。
- 对于计算,使用适当的最大数据类型以避免潜在的溢出。例如,在必要时对非常大的数字使用
long long int。
通过遵循这些原则,您可以在 C 语言及类似语言中编写更安全、更可靠的代码。
2. Const
在 C/C++ 中,const 关键字用于定义不可修改的变量。它的使用可以增强代码的安全性和可读性。下面是 const 的各种用法及其详细说明:
2.1 声明常量 (Declaring Constants)
const 可以用来声明常量,表示该变量的值不能被修改。
const int MAX_VALUE = 100; // MAX_VALUE 是一个常量,值为 100
// MAX_VALUE is a constant with a value of 100.
2.2 Constant Pointers
const 可以与指针结合使用,指明指针指向的内容是常量,或者指针本身是常量。
- 指向常量的指针: 不能通过该指针修改所指向的值。
const int* ptr = &MAX_VALUE; // ptr 指向一个常量整数
// *ptr = 200; // 错误:不能修改常量
// ptr points to a constant integer.
// Cannot modify the constant through ptr.
- 常量指针: 指针本身是常量,不能更改指向的地址。
int value = 50;
int* const ptr = &value; // ptr 是一个常量指针
*ptr = 60; // 正确:可以修改值
// ptr = &anotherValue; // 错误:不能修改指针
// ptr is a constant pointer.
// Can modify the value, but cannot change the pointer itself.
- 指向常量的常量指针: 既不能修改指针指向的内容,也不能更改指针本身。
const int* const ptr = &MAX_VALUE; // ptr 是指向常量的常量指针
// *ptr = 200; // 错误:不能修改常量
// ptr = &anotherValue; // 错误:不能修改指针
// ptr is a constant pointer to a constant integer.
// Cannot modify the constant or change the pointer.
2.3 函数参数 (Function Parameters)
在函数参数中使用 const 可以防止函数修改传入的参数,特别是对于引用和指针。
void printValue(const int* value) {
// value 指向的内容不能被修改
std::cout << *value << std::endl;
}
// The content pointed to by value cannot be modified.
以下是C++部分
2.4 常量成员函数 (Constant Member Functions)
在类中,常量成员函数是指不修改类的成员变量的函数。使用 const 修饰成员函数,表明该函数不会更改类的状态。
class MyClass {
public:
void show() const { // show 是一个常量成员函数
// this->value = 10; // 错误:不能修改成员变量
}
};
// show is a constant member function.
// Cannot modify member variables within this function.
2.5 常量对象 (Constant Objects)
如果你想创建一个不允许修改的对象,可以将对象声明为 const。
const MyClass obj; // obj 是一个常量对象
// obj.show(); // 正确,但无法修改 obj 的任何成员
// obj is a constant object.
// Can call show(), but cannot modify any members of obj.
2.6 常量表达式 (Constant Expressions)
C++11 引入了 constexpr 关键字,用于在编译时计算常量表达式,可以结合 const 使用。
constexpr int square(int x) {
return x * x; // 返回 x 的平方
}
const int result = square(5); // result 在编译时计算
// square returns the square of x.
// result is computed at compile time.
2.5 总结 (Summary)
- 常量声明:
const用于定义不可修改的变量。 Constant Declaration:constis used to define non-modifiable variables. - 常量指针:
const可与指针结合使用,指定指向的内容或指针本身是常量。 Constant Pointers:constcan be combined with pointers to specify that the content pointed to or the pointer itself is constant. - 函数参数: 在函数参数中使用
const防止修改。 Function Parameters: Useconstin function parameters to prevent modification. - 常量成员函数: 通过
const修饰成员函数,表示不修改类的状态。 Constant Member Functions: Useconstto indicate that a member function does not modify the state of the class. - 常量对象: 可以声明不允许修改的对象。 Constant Objects: You can declare objects that cannot be modified.
- 常量表达式: C++11 引入
constexpr,结合const使用。 Constant Expressions: C++11 introducedconstexpr, which can be used withconst.
使用 const 是一个良好的编程习惯,能够帮助你编写更安全、更高效的代码。 Using const is a good programming practice that helps you write safer and more efficient code.
3. C语言中的隐藏
#include <stdio.h>
int main(void) {
int b = 2;
printf("b = %d\n", b); // 输出: b = 2
{
int b = 3;
printf("b = %d\n", b); // 输出: b = 3
}
printf("b = %d\n", b); // 输出: b = 2
return 0;
}
- 第一次输出b:在
main函数的作用域中定义一个整型变量b,并将其初始化为2。这个变量b在main函数的整个范围内可见。
- 第二次输出b:创建了一个新的作用域(块作用域)。在这个块内部,定义了另一个同名的整型变量
b,并将其初始化为3。这个新的b会隐藏外部作用域中的b。
- 第三次输出b:结束了新创建的作用域。在这一点上,局部变量
b不再可用,外部作用域中的b重新变得可见。
4. C语言中的语法糖
syntactic sugar AKA 语法糖
a+=b; // a = a + b;
a-=b; // a = a - b;
a*=b; // a = a * b;
a/=10; // a = a / b;
//同理
i++; // 叫后置++,因为++在后面
++i; // 叫前置++,因为++在前面
i--;
--i;
扩展:我们后面在学习C++的时候会知道++i 前置++的效率高一些
示例代码
以下是一个示例代码,演示了 ++i 和 i++ 在自定义类中的区别:
#include <iostream>
class Counter {
public:
Counter() : count(0) {}
// 前缀自增 ++i
Counter& operator++() {
++count;
return *this;
}
// 后缀自增 i++
Counter operator++(int) {
Counter temp = *this; // 创建副本
++count;
return temp; // 返回副本
}
int getCount() const { return count; }
private:
int count;
};
int main() {
Counter c;
++c; // 前缀自增
std::cout << "Count after ++c: " << c.getCount() << std::endl; // 输出: 1
c++; // 后缀自增
std::cout << "Count after c++: " << c.getCount() << std::endl; // 输出: 2
return 0;
}
5. Bitwise operators (按位运算符)
a & b // AND
a | b // OR
a ^ b // XOR
~a // NOT
a << n // left shift
a >> n // right shift
Like arithmetic operators, there are in-place versions: e.g. a &= b;
a&&b //AND. Returns true if both a and b are true (non-zero)
a||b //OR. Returns true if either a or b are true (non-zero)
!a // NOT. Returns true if a is false. Returns false if a is true
扩展问题1:a&b 和 a&&b 有什么区别
& 和 && 是两种不同的操作符,它们在功能和用法上有显著的区别。
- 位运算符(
&)
- 定义:
&是位运算符(按位与运算符)。 - 用法:用于按位对两个整数进行与运算。
- 特点:对两个操作数的每一位进行比较,如果两个对应位都是
1,则结果位为1,否则为0。
示例
#include <iostream>
int main() {
int a = 5; // 二进制: 0101
int b = 3; // 二进制: 0011
int result = a & b; // 0101 & 0011 = 0001 (十进制: 1)
std::cout << "a & b = " << result << std::endl; // 输出: a & b = 1
return 0;
}
- 逻辑运算符(
&&)
- 定义:
&&是逻辑与运算符(短路与)。 - 用法:用于布尔逻辑运算,通常用于控制流(如条件语句)。
- 特点:如果第一个操作数为
false,则不会计算第二个操作数(短路特性)。整个表达式的结果只有在两个操作数都为true时才为true。
示例
#include <iostream>
bool conditionA() {
std::cout << "Evaluating condition A" << std::endl;
return true;
}
bool conditionB() {
std::cout << "Evaluating condition B" << std::endl;
return false;
}
int main() {
if (conditionA() && conditionB()) {
std::cout << "Both conditions are true." << std::endl;
} else {
std::cout << "At least one condition is false." << std::endl;
}
return 0;
}
输出
Evaluating condition A
Evaluating condition B
At least one condition is false.
- 总结
-
&(按位与) :- 用于整数的按位操作。
- 对每个二进制位进行比较。
-
&&(逻辑与) :- 用于布尔逻辑操作。
- 仅在两个条件都为
true时返回true,并具有短路特性。
使用场景
- 使用
&进行按位操作时,通常在处理位标志或执行位运算时使用。 - 使用
&&进行逻辑判断时,通常在控制程序流时使用,如条件语句、循环等。
扩展问题2:如何交换a 和 b 两个数 : 引用,指针,bitwise
#include <iostream>
// Swap using reference (C++)
void swapWithTemp(int& a, int& b) {
int temp = a; // Use reference to get the value of a
a = b; // Assign the value of b to a
b = temp; // Assign temp (the original value of a) to b
}
// Swap using pointers
void swapWithPointers(int* a, int* b) {
int temp = *a; // Dereference the pointer to get the value of a
*a = *b; // Assign the value of b to a
*b = temp; // Assign temp (the original value of a) to b
}
// Swap using XOR
void swapWithXOR(int* a, int* b) {
if (a != b) { // Ensure that a and b point to different addresses
// * has a higher precedence than ^ 优先级高
*a = *a ^ *b; // Step 1
*b = *a ^ *b; // Step 2
*a = *a ^ *b; // Step 3
}
}
// Another mathematical method that can easily cause overflow, not recommended
// Main function
int main() {
int a = 5;
int b = 3;
// Output initial values
std::cout << "Before swap: a = " << a << ", b = " << b << std::endl;
// Swap using a temporary variable
swapWithTemp(a, b);
std::cout << "After swap with temp: a = " << a << ", b = " << b << std::endl;
// Reassign values
a = 5; b = 3;
// Swap using pointers
swapWithPointers(&a, &b);
std::cout << "After swap with pointers: a = " << a << ", b = " << b << std::endl;
// Reassign values
a = 5; b = 3;
// Swap using XOR
swapWithXOR(&a, &b);
std::cout << "After swap with XOR: a = " << a << ", b = " << b << std::endl;
return 0;
}