目录
一、指针(Pointer):
1.1 动态内存管理:
1.2 数组和字符串操作:
1.3 函数参数传递:
1.4 数据结构实现(链表):
1.5 实现回调函数:
1.6 提高性能(直接内存访问示例):
二、对象(Object):
三、句柄(Handle):
四、套接字(Socket):
五、描述符(Descriptor):
六、实例(Instance):
七、令牌(Token):
八、签名(Signature):
嵌入式科普(14)指针---这些年嵌入式工程师也不容易理解的词语
一、指针(Pointer):
指针是C/C++中的一个重要概念,它存储了一个变量的内存地址。通过指针,可以直接访问和操作内存中的数据。
C语言示例:
#include <stdio.h>
int main() {
int x = 10;
int *ptr = &x; // ptr 是一个指向 x 的指针
printf("Value of x: %d\n", *ptr); // 输出 x 的值,使用 * 运算符解引用指针
*ptr = 20; // 通过指针修改 x 的值
printf("Value of x after modification: %d\n", x); // 输出修改后的 x 的值
return 0;
}
C语言中指针的六个重要应用的示例代码:
1.1 动态内存管理:
#include <stdio.h>
#include <stdlib.h>
int main() {
int *ptr = (int *)malloc(sizeof(int)); // 动态分配内存
if (ptr == NULL) {
perror("Memory allocation failed");
return 1;
}
*ptr = 42; // 使用指针设置值
printf("Value: %d\n", *ptr); // 输出值
free(ptr); // 释放内存
return 0;
}
1.2 数组和字符串操作:
#include <stdio.h>
int main() {
int array[] = {1, 2, 3, 4, 5};
int *ptr = array; // 指针指向数组首元素
for (int i = 0; i < 5; i++) {
printf("%d ", *(ptr + i)); // 使用指针遍历数组
}
printf("\n");
char str[] = "Hello";
char *str_ptr = str; // 指针指向字符串首字符
printf("%s\n", str_ptr); // 使用指针输出字符串
return 0;
}
1.3 函数参数传递:
#include <stdio.h>
void increment(int *value) {
(*value)++; // 通过指针修改值
}
int main() {
int num = 5;
increment(&num); // 传递变量的地址
printf("Incremented value: %d\n", num); // 输出修改后的值
return 0;
}
1.4 数据结构实现(链表):
#include <stdio.h>
#include <stdlib.h>
typedef struct Node {
int data;
struct Node *next;
} Node;
int main() {
Node *head = (Node *)malloc(sizeof(Node)); // 创建头节点
head->data = 1;
head->next = NULL;
Node *second = (Node *)malloc(sizeof(Node)); // 创建第二个节点
second->data = 2;
second->next = NULL;
head->next = second; // 将第二个节点链接到头节点
Node *current = head; // 使用指针遍历链表
while (current != NULL) {
printf("%d ", current->data);
current = current->next;
}
printf("\n");
// 释放链表内存(略)
return 0;
}
1.5 实现回调函数:
#include <stdio.h>
// 回调函数类型定义
typedef void (*Callback)(int);
// 回调函数实现
void print_value(int value) {
printf("Callback called with value: %d\n", value);
}
// 接受回调函数的函数
void process_data(int data, Callback callback) {
// ... 执行一些操作 ...
callback(data); // 调用回调函数
}
int main() {
process_data(10, print_value); // 传递回调函数作为参数
return 0;
}
1.6 提高性能(直接内存访问示例):
#include <stdio.h>
#include <time.h>
#define ARRAY_SIZE 1000000
int main() {
int array[ARRAY_SIZE];
int *ptr = array;
// 使用指针直接访问内存来填充数组
clock_t start = clock();
for (int i = 0; i < ARRAY_SIZE; i++) {
*(ptr + i) = i;
}
clock_t end = clock();
double time_taken_ptr = (double)(end - start) / CLOCKS_PER_SEC;
printf("Time taken using pointer: %f seconds\n", time_taken_ptr);
// 使用数组索引来填充数组
start = clock();
for (int i = 0; i < ARRAY_SIZE; i++) {
array[i] = i;
}
end = clock();
double time_taken_index = (double)(end - start) / CLOCKS_PER_SEC;
printf("Time taken using array index: %f seconds\n", time_taken_index);
// 输出两种方法的性能差异(如果有的话)
if (time_taken_ptr < time_taken_index) {
printf("Pointer access was faster.\n");
} else if (time_taken_ptr > time_taken_index) {
printf("Array index access was faster.\n");
} else {
printf("No significant difference.\n");
}
return 0;
}
二、对象(Object):
在C++中,对象是类的实例,具有属性和方法。对象是面向对象编程(OOP)的基本构建块。
C++示例:
#include <iostream>
#include <string>
class Dog {
public:
std::string name;
int age;
void bark() {
std::cout << name << " says Woof!" << std::endl;
}
};
int main() {
Dog myDog; // myDog 是 Dog 类的一个对象
myDog.name = "Buddy";
myDog.age = 3;
myDog.bark(); // 调用对象的方法
return 0;
}
三、句柄(Handle):
句柄通常用于表示系统资源(如文件、网络连接等)的引用。在C/C++中,句柄通常是以某种形式(如整数或指针)表示的标识符,用于在API调用中引用这些资源。
由于句柄的具体实现取决于操作系统和API,因此很难给出一个通用的C/C++示例。但通常,句柄是通过调用系统API函数(如打开文件或创建网络连接)获得的,并在后续的操作中用作参数。
#include <stdio.h>
#include <stdlib.h>
// 假设我们有一个内存池,我们使用整数句柄来引用它
typedef struct {
int* data;
size_t size;
} MemoryBlock;
// 内存池管理
typedef struct {
MemoryBlock* blocks;
size_t capacity;
size_t used;
} MemoryPool;
// 初始化内存池
void initMemoryPool(MemoryPool* pool, size_t capacity) {
pool->blocks = (MemoryBlock*)malloc(capacity * sizeof(MemoryBlock));
pool->capacity = capacity;
pool->used = 0;
}
// 分配内存并返回句柄
int allocateMemory(MemoryPool* pool, size_t size) {
if (pool->used >= pool->capacity) {
fprintf(stderr, "Memory pool is full!\n");
return -1; // 返回错误句柄
}
MemoryBlock* newBlock = &pool->blocks[pool->used];
newBlock->data = (int*)malloc(size * sizeof(int));
newBlock->size = size;
int handle = (int)pool->used; // 句柄是内存在blocks数组中的索引
pool->used++;
return handle;
}
// 使用句柄获取内存块的指针
int* getMemoryByHandle(MemoryPool* pool, int handle) {
if (handle < 0 || handle >= pool->used) {
fprintf(stderr, "Invalid handle!\n");
return NULL;
}
return pool->blocks[handle].data;
}
// 释放内存池中的内存块
void freeMemoryByHandle(MemoryPool* pool, int handle) {
if (handle < 0 || handle >= pool->used) {
fprintf(stderr, "Invalid handle!\n");
return;
}
free(pool->blocks[handle].data);
pool->blocks[handle].data = NULL;
pool->blocks[handle].size = 0;
}
// 清理内存池
void cleanupMemoryPool(MemoryPool* pool) {
for (size_t i = 0; i < pool->used; i++) {
free(pool->blocks[i].data);
}
free(pool->blocks);
pool->blocks = NULL;
pool->capacity = 0;
pool->used = 0;
}
int main() {
MemoryPool pool;
initMemoryPool(&pool, 10); // 初始化一个容量为10的内存池
int handle = allocateMemory(&pool, 5); // 分配5个整数的内存块,并获取句柄
if (handle != -1) {
int* memory = getMemoryByHandle(&pool, handle); // 使用句柄获取内存块指针
if (memory) {
for (size_t i = 0; i < 5; i++) {
memory[i] = i * i; // 在内存中存储平方值
}
// ... 使用内存块 ...
freeMemoryByHandle(&pool, handle); // 释放内存块
}
}
cleanupMemoryPool(&pool); // 清理内存池
return 0;
}
#include <stdio.h>
#include <stdlib.h>
int main() {
// 打开文件以写入数据,获取文件句柄(FILE *指针)
FILE *fileHandle = fopen("example.txt", "w");
if (fileHandle == NULL) {
perror("Error opening file for writing");
return EXIT_FAILURE;
}
// 使用文件句柄写入数据到文件
const char *text = "Hello, World!\n";
fputs(text, fileHandle);
// 关闭文件句柄
fclose(fileHandle);
// 打开同一个文件以读取数据
fileHandle = fopen("example.txt", "r");
if (fileHandle == NULL) {
perror("Error opening file for reading");
return EXIT_FAILURE;
}
// 读取文件内容
char buffer[1024];
while (fgets(buffer, sizeof(buffer), fileHandle) != NULL) {
printf("%s", buffer);
}
// 关闭文件句柄
fclose(fileHandle);
return EXIT_SUCCESS;
}
四、套接字(Socket):
套接字是网络编程中的基本概念,用于实现不同计算机之间的通信。在C/C++中,套接字通常通过系统调用和相关的网络库来创建和使用。
C语言示例(使用BSD套接字API):
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
int main() {
int sockfd;
struct sockaddr_in serv_addr;
char *hello = "Hello from client";
sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (sockfd < 0) {
perror("ERROR opening socket");
exit(1);
}
bzero((char *) &serv_addr, sizeof(serv_addr));
serv_addr.sin_family = AF_INET;
serv_addr.sin_addr.s_addr = inet_addr("127.0.0.1");
serv_addr.sin_port = htons(12345);
if (connect(sockfd, (struct sockaddr *) &serv_addr, sizeof(serv_addr)) < 0) {
perror("ERROR connecting");
exit(1);
}
send(sockfd, hello, strlen(hello), 0);
close(sockfd);
return 0;
}
五、描述符(Descriptor):
描述符通常用于表示打开的文件、网络连接或其他I/O资源的标识符。在C/C++中,文件描述符是常见的例子,它们是由open系统调用返回的整数,用于在后续的文件操作中标识特定的文件。
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
int main() {
// 打开文件以写入数据,获取文件描述符
int fd = open("example.txt", O_WRONLY | O_CREAT, S_IRUSR | S_IWUSR);
if (fd == -1) {
perror("Error opening file for writing");
return EXIT_FAILURE;
}
// 使用文件描述符写入数据到文件
const char *text = "Hello, World!\n";
ssize_t bytesWritten = write(fd, text, strlen(text));
if (bytesWritten == -1) {
perror("Error writing to file");
close(fd); // 关闭文件描述符
return EXIT_FAILURE;
}
// 关闭文件描述符
close(fd);
// 打开同一个文件以读取数据
fd = open("example.txt", O_RDONLY);
if (fd == -1) {
perror("Error opening file for reading");
return EXIT_FAILURE;
}
// 读取文件内容
char buffer[1024];
ssize_t bytesRead = read(fd, buffer, sizeof(buffer) - 1); // 减1以保留空间给'\0'
if (bytesRead == -1) {
perror("Error reading from file");
close(fd); // 关闭文件描述符
return EXIT_FAILURE;
}
// 在buffer末尾添加null终止符
buffer[bytesRead] = '\0';
// 打印读取到的内容
printf("Read from file: %s", buffer);
// 关闭文件描述符
close(fd);
return EXIT_SUCCESS;
}
六、实例(Instance):
在面向对象的编程中,实例是类的一个具体对象。当创建类的一个新对象时,就得到了该类的一个实例。
C++示例(继续上面的Dog类):
#include <iostream>
#include <string>
class Dog {
public:
std::string name;
int age;
Dog(std::string name, int age) : name(name), age(age) {} // 构造函数
void bark() {
std::cout << name << " says Woof!" << std::endl;
}
};
int main() {
Dog myDog("Buddy", 3); // myDog 是 Dog 类的一个实例
myDog.bark(); // 调用实例的方法
return 0;
}
在C语言中,"实例"(instance)这个术语通常与面向对象编程(OOP)相关,而C语言本身并不是一种面向对象的编程语言。然而,我们可以模拟面向对象的某些概念,比如结构体(struct)可以被视为类的实例。以下是一个使用结构体作为"实例"的C语言例子:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
// 定义一个结构体,类似于一个类
typedef struct {
char name[50];
int age;
} Person;
// 创建一个函数,用于初始化Person实例
void createPerson(Person *person, const char *name, int age) {
strcpy(person->name, name);
person->age = age;
}
// 创建一个函数,用于打印Person实例的信息
void printPerson(const Person *person) {
printf("Name: %s\n", person->name);
printf("Age: %d\n", person->age);
}
int main() {
// 创建Person的实例(结构体变量)
Person alice;
// 初始化Person实例
createPerson(&alice, "Alice", 30);
// 打印Person实例的信息
printPerson(&alice);
// 创建另一个Person实例
Person bob;
createPerson(&bob, "Bob", 25);
printPerson(&bob);
return 0;
}
七、令牌(Token):
在计算机编程和操作系统中,令牌通常用于表示对某种资源的访问权限。例如,在并发编程中,令牌可以用来控制对共享资源的访问,实现同步机制。
由于令牌的具体实现和应用场景多种多样,很难提供一个通用的C/C++示例。令牌的使用通常依赖于特定的库或框架,例如线程同步原语(如信号量或互斥锁)可能使用内部令牌来控制资源的访问。
在C语言中,通常不会直接使用“令牌”(Token)这个术语,因为令牌是编译器在处理源代码时的一个内部概念。编译器会把源代码文本分解成一系列的令牌,这些令牌是源代码的最小语法单元,例如关键字、标识符、运算符、分隔符、字面量等。
然而,如果我们想要模拟令牌的概念,并手动解析和处理一些简单的“令牌流”,我们可以编写一个简单的C语言程序来模拟这个过程。以下是一个简单的例子,该程序定义了一些令牌类型,并模拟了令牌的产生和处理过程:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
// 定义令牌类型
typedef enum {
TOKEN_IDENTIFIER,
TOKEN_NUMBER,
TOKEN_PLUS,
TOKEN_MINUS,
TOKEN_MUL,
TOKEN_DIV,
TOKEN_EOL, // 行结束符
TOKEN_UNKNOWN, // 未知令牌
TOKEN_EOF // 文件结束符
} TokenType;
// 定义令牌结构体
typedef struct {
TokenType type;
char value[50];
} Token;
// 假设的令牌生成函数,这里简单模拟
Token generate_token(const char **input) {
Token token;
token.type = TOKEN_UNKNOWN;
strcpy(token.value, "");
// 跳过空白字符
while (**input == ' ') (*input)++;
if (**input == '\0') {
token.type = TOKEN_EOF;
return token;
}
// 识别不同的令牌类型
switch (**input) {
case '+':
token.type = TOKEN_PLUS;
break;
case '-':
token.type = TOKEN_MINUS;
break;
case '*':
token.type = TOKEN_MUL;
break;
case '/':
token.type = TOKEN_DIV;
break;
case '\n':
token.type = TOKEN_EOL;
break;
default:
if (isdigit(**input)) {
token.type = TOKEN_NUMBER;
while (isdigit(**input)) {
token.value[strlen(token.value)] = **input;
(*input)++;
}
token.value[strlen(token.value)] = '\0';
} else if (isalpha(**input)) {
token.type = TOKEN_IDENTIFIER;
while (isalnum(**input)) {
token.value[strlen(token.value)] = **input;
(*input)++;
}
token.value[strlen(token.value)] = '\0';
} else {
// 未知字符
token.type = TOKEN_UNKNOWN;
(*input)++;
}
}
(*input)++; // 移动到下一个字符
return token;
}
int main() {
const char *input = "a + 5 * b\n"; // 示例输入
Token token;
do {
token = generate_token(&input);
switch (token.type) {
case TOKEN_IDENTIFIER:
printf("Identifier: %s\n", token.value);
break;
case TOKEN_NUMBER:
printf("Number: %s\n", token.value);
break;
case TOKEN_PLUS:
printf("Operator: +\n");
break;
case TOKEN_MINUS:
printf("Operator: -\n");
break;
case TOKEN_MUL:
printf("Operator: *\n");
break;
case TOKEN_DIV:
printf("Operator: /\n");
break;
case TOKEN_EOL:
printf("End of line\n");
break;
case TOKEN_UNKNOWN:
printf("Unknown token\n");
break;
case TOKEN_EOF:
printf("End of file\n");
break;
}
} while (token.type != TOKEN_EOF);
return 0;
}
在这个例子中,我们定义了一个Token结构体来存储令牌的类型和值。generate_token函数模拟了从输入字符串中生成令牌的过程。在main函数中,我们遍历输入的字符串,生成并打印出每一个令牌。
请注意,这个例子是非常简化的,并且只是为了演示目的。在实际的编译器中,令牌化(tokenization)是一个更复杂的过程,涉及词法分析(lexical analysis)和更严格的语法规则。此外,编译器通常还会
八、签名(Signature):
签名通常用于验证数据或消息的完整性和来源。在编程中,它可能指函数的签名,即函数的名称和参数类型列表,用于唯一标识一个函数。
C++示例(函数签名):
#include <iostream>
// 这是一个函数签名,它声明了一个名为 add 的函数,接受两个 int 参数,并返回一个 int 结果
int add(int a, int b);
int add(int a, int b) { // 这是函数的定义,它实现了上述签名
return a + b;
}
int main() {
int sum = add(5, 3); // 调用函数,使用其签名匹配正确的函数定义
std::cout << "Sum: " << sum << std::endl;
return 0;
}
嵌入式科普15
嵌入式科普 · 目录
上一篇嵌入式科普(13)汽车OTA---这些年被大众熟知的嵌入式词语