大家好,我是柠檬哥
欢迎关注@程序员柠檬橙编程路上不迷路,私信发送以下关键字获取编程资源:
发送
2025年07月04日
大家好,今天给大家介绍C语言中的数组:声明、初始化和操作,文章末尾附有分享大家一个资料包,差不多150多G。里面学习内容、面经、项目都比较新也比较全!
2025年07月04日
C语言的函数是什么?
先看一下这段代码,我把排序写在了main函数里面。如果说我现在有更多的功能都写在main函数,你肯定会阅读不下去这个代码。
函数是一组一起执行一个任务的语句,每个C程序都至少有一个函数,即主函数main(可以把一些代码分配到不同的函数中。函数还有很多叫法,比如方法、子程序等。暂停先看一下定义,第二行和第三行是函数的声明,函数声明的写法就是函数的返回类型+函数名+形式参数+分号。函数第12行和第21行是我写的两个不同功能的函数。这里的结构就是:返回类型+函数名+形式参数+方法体。12行的返回类型是个一个整数,21行void表示无返回值。
2025年07月04日
以下是使用C语言实现的2048小游戏代码:
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <conio.h> // 用于Windows平台的_getch()
#define SIZE 4
#define TARGET 2048
// 游戏板结构体
typedef struct
{
int board[SIZE][SIZE];
int score;
int game_over;
int win;
} Game;
// 方向枚举
enum Direction
{
UP,
DOWN,
LEFT,
RIGHT
};
void display(const Game *game);
int check_game_over(const Game *game);
void add_new_tile(Game *game);
void move(Game *game, enum Direction dir);
// 初始化游戏
void init_game(Game *game)
{
// 清空棋盘
for (int i = 0; i < SIZE; i++)
{
for (int j = 0; j < SIZE; j++)
{
game->board[i][j] = 0;
}
}
game->score = 0;
game->game_over = 0;
game->win = 0;
// 生成两个初始数字
add_new_tile(game);
add_new_tile(game);
}
// 添加新数字(90%概率生成2,10%生成4)
void add_new_tile(Game *game)
{
int empty_cells[SIZE * SIZE][2];
int count = 0;
// 查找所有空格子
for (int i = 0; i < SIZE; i++)
{
for (int j = 0; j < SIZE; j++)
{
if (game->board[i][j] == 0)
{
empty_cells[count][0] = i;
empty_cells[count][1] = j;
count++;
}
}
}
if (count > 0)
{
// 随机选择一个空格子
int index = rand() % count;
int value = (rand() % 10 < 9) ? 2 : 4; // 概率分配
game->board[empty_cells[index][0]][empty_cells[index][1]] = value;
}
}
// 打印游戏界面
void display(const Game *game)
{
system("cls"); // 清屏(Windows)
printf("Score: %d\n", game->score);
printf("-------------------------\n");
for (int i = 0; i < SIZE; i++)
{
printf("|");
for (int j = 0; j < SIZE; j++)
{
if (game->board[i][j] == 0)
{
printf(" |");
}
else
{
printf("%4d|", game->board[i][j]);
}
}
printf("\n-------------------------\n");
}
}
// 检查游戏是否结束
int check_game_over(const Game *game)
{
// 检查是否有空格子
for (int i = 0; i < SIZE; i++)
{
for (int j = 0; j < SIZE; j++)
{
if (game->board[i][j] == 0)
return 0;
}
}
// 检查是否有相邻相同数字
for (int i = 0; i < SIZE; i++)
{
for (int j = 0; j < SIZE; j++)
{
int current = game->board[i][j];
// 检查右侧和下侧
if (j < SIZE - 1 && current == game->board[i][j + 1])
return 0;
if (i < SIZE - 1 && current == game->board[i + 1][j])
return 0;
}
}
return 1;
}
// 移动并合并数字(核心逻辑)
void move(Game *game, enum Direction dir)
{
int moved = 0;
int temp_board[SIZE][SIZE];
// 复制当前棋盘状态
for (int i = 0; i < SIZE; i++)
{
for (int j = 0; j < SIZE; j++)
{
temp_board[i][j] = game->board[i][j];
}
}
// 根据方向处理移动
switch (dir)
{
case LEFT:
for (int i = 0; i < SIZE; i++)
{
int pos = 0;
int last_merged = -1;
for (int j = 0; j < SIZE; j++)
{
if (temp_board[i][j] != 0)
{
if (pos > 0 && temp_board[i][pos - 1] == temp_board[i][j] && last_merged != pos - 1)
{
// 合并
temp_board[i][pos - 1] *= 2;
game->score += temp_board[i][pos - 1];
temp_board[i][j] = 0;
last_merged = pos - 1;
moved = 1;
}
else
{
if (pos != j)
moved = 1;
temp_board[i][pos] = temp_board[i][j];
pos++;
}
}
}
// 填充右侧空白
while (pos < SIZE)
{
if (temp_board[i][pos] != 0)
moved = 1;
temp_board[i][pos++] = 0;
}
}
break;
case DOWN:
// 按列处理,从下往上扫描
for (int j = 0; j < SIZE; j++)
{ // 遍历每一列
int pos = SIZE - 1; // 从最底行开始填充
int last_merged = SIZE; // 初始化最后合并位置(超出范围)
for (int i = SIZE - 1; i >= 0; i--)
{ // 从下往上扫描
if (temp_board[i][j] != 0)
{ // 发现非空单元格
// 检查是否可以合并
if (pos < SIZE - 1 && // 确保不是第一个元素
temp_board[pos + 1][j] == temp_board[i][j] && // 值相同
last_merged != pos + 1)
{ // 防止重复合并
// 执行合并
temp_board[pos + 1][j] *= 2;
game->score += temp_board[pos + 1][j]; // 更新分数
temp_board[i][j] = 0; // 清空当前位置
last_merged = pos + 1; // 记录合并位置
moved = 1; // 标记发生移动
}
else
{
// 无法合并则移动元素
if (pos != i)
{ // 位置发生变化
temp_board[pos][j] = temp_board[i][j];
temp_board[i][j] = 0; // 清空原位置
moved = 1;
}
pos--; // 更新填充位置
}
}
}
// 填充上方空白(非必需但更安全)
while (pos >= 0)
{
if (temp_board[pos][j] != 0)
moved = 1;
temp_board[pos--][j] = 0;
}
}
break;
case RIGHT:
// 按行处理,从右往左扫描
for (int i = 0; i < SIZE; i++)
{ // 遍历每一行
int pos = SIZE - 1; // 从最右列开始填充
int last_merged = SIZE; // 初始化最后合并位置(超出范围)
for (int j = SIZE - 1; j >= 0; j--)
{ // 从右往左扫描
if (temp_board[i][j] != 0)
{ // 发现非空单元格
// 检查是否可以合并
if (pos < SIZE - 1 && // 确保不是第一个元素
temp_board[i][pos + 1] == temp_board[i][j] && // 值相同
last_merged != pos + 1)
{ // 防止重复合并
// 执行合并
temp_board[i][pos + 1] *= 2;
game->score += temp_board[i][pos + 1]; // 更新分数
temp_board[i][j] = 0; // 清空当前位置
last_merged = pos + 1; // 记录合并位置
moved = 1; // 标记发生移动
}
else
{
// 无法合并则移动元素
if (pos != j)
{ // 位置发生变化
temp_board[i][pos] = temp_board[i][j];
temp_board[i][j] = 0; // 清空原位置
moved = 1;
}
pos--; // 更新填充位置
}
}
}
// 填充左侧空白(非必需但更安全)
while (pos >= 0)
{
if (temp_board[i][pos] != 0)
moved = 1;
temp_board[i][pos--] = 0;
}
}
break;
case UP:
// 按列处理,从上往下扫描
for (int j = 0; j < SIZE; j++)
{ // 遍历每一列
int pos = 0; // 从最顶行开始填充
int last_merged = -1; // 初始化最后合并位置(无效位置)
for (int i = 0; i < SIZE; i++)
{ // 从上往下扫描
if (temp_board[i][j] != 0)
{ // 发现非空单元格
// 检查是否可以合并
if (pos > 0 && // 确保不是第一个元素
temp_board[pos - 1][j] == temp_board[i][j] && // 值相同
last_merged != pos - 1)
{ // 防止重复合并
// 执行合并
temp_board[pos - 1][j] *= 2;
game->score += temp_board[pos - 1][j]; // 更新分数
temp_board[i][j] = 0; // 清空当前位置
last_merged = pos - 1; // 记录合并位置
moved = 1; // 标记发生移动
}
else
{
// 无法合并则移动元素
if (pos != i)
{ // 位置发生变化
temp_board[pos][j] = temp_board[i][j];
temp_board[i][j] = 0; // 清空原位置
moved = 1;
}
pos++; // 更新填充位置
}
}
}
// 填充下方空白(非必需但更安全)
while (pos < SIZE)
{
if (temp_board[pos][j] != 0)
moved = 1;
temp_board[pos++][j] = 0;
}
}
break;
// 其他方向处理类似(代码略)
// ...
}
// 更新棋盘状态
if (moved)
{
for (int i = 0; i < SIZE; i++)
{
for (int j = 0; j < SIZE; j++)
{
game->board[i][j] = temp_board[i][j];
}
}
add_new_tile(game);
game->game_over = check_game_over(game);
}
}
// 获取用户输入
enum Direction get_input()
{
int c = _getch();
switch (c)
{
case 72:
return UP; // 方向键上
case 80:
return DOWN; // 方向键下
case 75:
return LEFT; // 方向键左
case 77:
return RIGHT; // 方向键右
case 'q':
exit(0); // 退出游戏
default:
return get_input(); // 无效输入重新获取
}
}
int main()
{
srand(time(NULL)); // 初始化随机种子
Game game;
init_game(&game);
while (!game.game_over && !game.win)
{
display(&game);
enum Direction dir = get_input();
move(&game, dir);
// 检查是否达成2048
for (int i = 0; i < SIZE; i++)
{
for (int j = 0; j < SIZE; j++)
{
if (game.board[i][j] == TARGET)
{
game.win = 1;
break;
}
}
}
}
display(&game);
if (game.win)
{
printf("Congratulations! You've reached %d!\n", TARGET);
}
else
{
printf("Game Over! Final score: %d\n", game.score);
}
return 0;
}
2025年07月04日
前言
写PLC程序,越来越觉得结构化文本编程语言(ST)给PC编程带来的便利,在处理一些数据上,可以写的更加灵活。所以,在项目PLC选型上,我都会优先选择支持结构化文本的PLC。
国内有些厂商推出了一些较为高端一些的PLC,开发环境集成在Codesys平台(例如禾川、信捷);也有的PLC的开发环境集成在Visual Studio平台中(例如倍福)。
2025年07月04日
以下是一些让C语言老师感到非常气愤的极其低级错误的C语言代码示例:
int a = 5 // 缺少分号
int b = 10;
忘记在语句结束处添加分号是一个非常常见的错误,但它会导致编译错误。
2025年07月04日
单例模式是一种创建型设计模式,它确保一个类只有一个实例,并提供一个全局访问点。 它是最简单和最常用的设计模式之一,本文介绍C语言及C++实现的3种单例模式代码。
2025年07月04日
以下是C语言实现正则表达式的代码:
#include <stdbool.h>
#include <string.h>
#include <stdlib.h>
bool isMatch(char* s, char* p) {
int m = strlen(s);
int n = strlen(p);
// 检查模式p的合法性,避免无效的'*'
for (int i = 0; i < n; ++i) {
if (p[i] == '*' && (i == 0 || p[i-1] == '*')) {
return false;
}
}
// 创建动态规划表,dp[i][j]表示s的前i个字符和p的前j个字符是否匹配
bool *dp = (bool *)calloc((m+1) * (n+1), sizeof(bool));
if (!dp) {
return false; // 内存分配失败,但题目通常保证不会出现
}
// 初始化基础情况:空字符串匹配空模式
dp[0] = true;
// 初始化第一行,处理模式p可以匹配空字符串的情况(如"a*")
for (int j = 1; j <= n; ++j) {
if (p[j-1] == '*') {
dp[j] = dp[j-2]; // 匹配0次的情况
}
}
// 填充dp表
for (int i = 1; i <= m; ++i) {
for (int j = 1; j <= n; ++j) {
if (p[j-1] == '*') {
// 处理'*'的情况,考虑匹配0次或多次
bool matchZero = dp[i*(n+1) + (j-2)];
bool matchMore = (p[j-2] == '.' || s[i-1] == p[j-2]) && dp[(i-1)*(n+1) + j];
dp[i*(n+1)+j] = matchZero || matchMore;
} else {
// 处理普通字符或'.'的情况
bool currentMatch = (p[j-1] == '.' || s[i-1] == p[j-1]);
dp[i*(n+1)+j] = currentMatch && dp[(i-1)*(n+1) + (j-1)];
}
}
}
bool result = dp[m*(n+1) + n];
free(dp);
return result;
}
void test(const char *s, const char *p, bool expected) {
bool result = isMatch((char*)s, (char*)p);
printf("s=\"%s\", p=\"%s\" -> %s (Expected %s)\n",
s, p,
result ? "true" : "false",
expected ? "true" : "false");
}
int main() {
// 完全匹配
test("aa", "aa", true);
// '.' 匹配任意字符
test("ab", "a.", true);
// '*' 匹配零次
test("a", "ab*", true); // b* 匹配零次
// '*' 匹配多次
test("aaa", "a*", true); // a* 匹配三次
test("ab", "a*b", true); // a* 匹配一次
// 复杂组合
test("aab", "c*a*b", true); // c* 匹配零次,a* 匹配两次
test("mississippi", "mis*is*p*.", false); // 无法匹配
// 无效模式
test("a", "a**", false); // 连续 '*' 非法
// 空字符串
test("", "", true); // 空字符串匹配空模式
test("", "a*", true); // a* 匹配零次
// 边界情况
test("a", ".*", true); // .* 匹配任意字符
return 0;
}
2025年07月04日
冒泡排序法的基本思路为:每次将相邻的两个数比较,将小的调在前面。举个例子,如果有6个数:9,8,5,4,2,0。第一次先将最前面的两个数9和8对调。第二次将第2个数和第3个数对调(9和5)······如此共进行5次得到8,5,4,2,0,9的顺序,可以看到:最大的数9已经沉到了最底下成为了最下面的一个数,而小的数“上升”。