从0到开始<七>:字符串相关及位运算

本文探讨了程序设计中的优化技巧,包括字符串处理函数、循环右移操作等,并提供了相应的测试程序验证效果。

程序一:编写函数any(s1,s2),将字符串s2中的任意字符在字符串s1中第一次出现的位置作为结果返回。如果s1中不包含s2中的字符,则返回-1。(标准库函数strpbrk(其头文件是string.h)具有同样的功能,但它返回的是指向该位置的指针)

解法一:O(n*m)的时间复杂度,这个简单

int any(char s1[], char s2[])
{
  int i;
  int j;
  int pos;

  pos = -1;

  for(i = 0; pos == -1 && s1[i] != '\0'; i++)
  {
    for(j = 0; pos == -1 && s2[j] != '\0'; j++)
    {
      if(s2[j] == s1[i])
      {
        pos = i;
      }
    } 
  }

  return pos;
}

解法二:O(n+m) 的时间复杂度

int any(char *s1, char *s2)
{
	int i = 0;
	char array[256] = {0};
	
	if (s1 == NULL)
	{
		if (s2 == NULL) return 0;
		else return -1;
	}
	
	while (*s2 != '\0')
	{
		array[*s2] = 1;
		s2++;
	}
	
	while (s1[i] != '\0')
	{
		if (array[ s1[i] ] == 1)
			return i;
		i++;
	}
	
	return -1;  // 容易漏掉哦 
}
下面看下测试程序:学习学习

#include <string.h>
#include <stdio.h>

int main(void)
{
  char *leftstr[] =
  {
    "",
    "a",
    "antidisestablishmentarianism",
    "beautifications",
    "characteristically",
    "deterministically",
    "electroencephalography",
    "familiarisation",
    "gastrointestinal",
    "heterogeneousness",
    "incomprehensibility",
    "justifications",
    "knowledgeable",
    "lexicographically",
    "microarchitectures",
    "nondeterministically",
    "organizationally",
    "phenomenologically",
    "quantifications",
    "representationally",
    "straightforwardness",
    "telecommunications",
    "uncontrollability",
    "vulnerabilities",
    "wholeheartedly",
    "xylophonically",
    "youthfulness",
    "zoologically"
  };
  char *rightstr[] =
  {
    "",
    "a",
    "the",
    "quick",
    "brown",
    "dog",
    "jumps",
    "over",
    "lazy",
    "fox",
    "get",
    "rid",
    "of",
    "windows",
    "and",
    "install",
    "linux"
  };

  size_t numlefts = sizeof leftstr / sizeof leftstr[0];
  size_t numrights = sizeof rightstr / sizeof rightstr[0];
  size_t left = 0;
  size_t right = 0;

  int passed = 0;
  int failed = 0;

  int pos = -1;
  char *ptr = NULL;

  for(left = 0; left < numlefts; left++)
  {
    for(right = 0; right < numrights; right++)
    {
      pos = any(leftstr[left], rightstr[right]);
      ptr = strpbrk(leftstr[left], rightstr[right]);

      if(-1 == pos)
      {
        if(ptr != NULL)
        {
          printf("Test %d/%d failed.\n", left, right);
          ++failed;
        }
        else
        {
          printf("Test %d/%d passed.\n", left, right);
          ++passed;
        }
      }
      else
      {
        if(ptr == NULL)
        {
          printf("Test %d/%d failed.\n", left, right);
          ++failed;
        }
        else
        {
          if(ptr - leftstr[left] == pos)
          {
            printf("Test %d/%d passed.\n", left, right);
            ++passed;
          }
          else
          {
            printf("Test %d/%d failed.\n", left, right);
            ++failed;
          }
        }
      }
    }
  }
  printf("\n\nTotal passes %d, fails %d, total tests %d\n",
         passed,
         failed,
         passed + failed);
  return 0;
}

程序二:编写一个函数rightrot(x,n),该函数返回将x循环右移(即从最右端移出的位将从最左端移入)n(二进制)位后所得到的值

解法一:正常思路

unsigned rightrot(unsigned x, unsigned n)
{
    while (n > 0) {
        if ((x & 1) == 1)  // 移出的位是1
            x = (x >> 1) | ~(~0U >> 1);
        else
            x = (x >> 1);
        n--;
    }
    return x;
}
解法二:很不错,主要是最后一句,好好领悟
unsigned int rightrot(unsigned int x, unsigned int n)
{
   size_t s = sizeof(x) * CHAR_BIT; //多少位 
   size_t p;

	/*限制位到0~s-1*/
   if(n < s)
       p = n; 
   else
       p = n % s;

   if((0 == x) || (0 == p))
       return x;

   return (x >> p) | (x << (s - p));
}

测试程序:

#include <stdio.h>

int main(void)
{
	unsigned x;
  	int n;
  	for(x = 0; x < 700; x += 49)
    	for(n = 1; n < 8; n++)
      		printf("%u, %d: %u\n", x, n, rightrot(x, n));
  	return 0;
}

先不搞这个,语法的定义和语义的定义:<程序> → <变量说明部分> <语句部分> ←说明:<程序>是开始符号 <变量说明部分> → <变量说明部分> <变量说明语句> ; | <变量说明语句> ; <变量说明语句> → <变量说明> <标识符列表> <变量说明> → int | string | boo l <标识符列表> → <标识符列表> , <标识符> | <标识符> <标识符> → $ <字母> <字母序列> $ <语句部分> → <语句部分> ; <语句> | <语句> <语句> → <赋值语句>|<条件语句>|<循环语句> <赋值语句> → <标识符> := <表达式> <表达式> → <disjunction> <disjunction> → < conjunction > | <disjunction> or < conjunction > < conjunction > → < inversion > | < conjunction > and < inversion > < inversion > → not < inversion > | < 关系表达式 > < 关系表达式 > → <算术表达式> <关系运算符> <算术表达式> | <算术表达式> <算术表达式> → <term> | <算术表达式> <加法运算符> <term> <term> → <factor>| <term> <乘法运算符> <factor> <factor> → <标识符> | true | false | <字符串> | <整数数列> | ( <表达式> ) <关系运算符> → < | > | <> | >= | <= | == <加法运算符> → + | - <乘法运算符> → * | / | % <字符串> → “<字母序列> ” <字母序列> → <字母序列><数字>|<字母序列><字母>| ε <整数数列> → <整数数列><数字>|<数字> <条件语句> → if (<表达式>)<嵌套语句> else <嵌套语句> <循环语句> → while (<表达式>) : <嵌套语句> <嵌套语句> → <语句>; |<复合语句> <复合语句> → { <语句部分> } <字母> → a|b|c|d|e|f|g|h|i |j|k| l |m|n|o|p|q|r|s|t|u|v|w|x|y|z <数字> → 0|1 |2|3|4|5|6|7|8|9 -----------------------------------语义定义------------------------------- l 声明语句:可以定义整数(int), 字符串(string)和布尔型(bool)的变量。 l 赋值语句:将整数数列赋值给整数变量,字符串内的字母序列赋值给字符串变量, true 和 false 赋值给布尔型变量。注意, 赋值时应该进行变量类型检查。 l 表达式: 布尔型变量或布尔值之间可以进行逻辑运算(or,and,not),关系运算(比较大小)和算术运算(加减等)。 逻辑运算的结果是布尔值; 关系运算时,true 相当于 1,false 相当于 0,计算结果是布尔值; 算术运算时,同样 true 是 1,false 是 0,计算结果是整数。 整数变量或整数数列之间可以进行关系运算和算数运算。 关系运算的计算结果是布尔值; 算数运算的计算结果是整数; 乘法可能造成变量值溢出, 因此计算结果最多保留前 4 位; 减法结果的最小值若小于 0,则重置为 0(确保不出现负数); /是整除,注意不能除0; %是取余,注意不能除0字符串变量或字符串之间可以进行关系运算和部分算数运算。其中: 关系运算的比较字符串的长度, 结果是布尔值; “+”运算的左右均为字符串时, 连接两个字符串,结果是字符串; “*”运算的左边是字符串,右边是整数数列时,将字符串重复整数次,结果是字符串;其中,重复 0 次的结果是空字符串;重复运算可能造成变量值溢出, 因此最多重复 2 次。 上述之外的组合均为语义错误。 l 条件语句:if 条件为 true 则实行第一个嵌套语句,否则实行 else 后的嵌套语句。 l 循环语句:while 条件为 true 则继续循环,否则跳出循环。这些是什么,满足了吗
最新发布
11-18
你给出的是一套完整的 **编译器语言规范**,包括: - ✅ 上下文无关文法(CFG)—— 语法定义 - ✅ 语义规则描述 —— 类型、运算、赋值、控制流等行为 我们来逐条分析:这套语言是否已经满足?你现在处于什么阶段? --- ## 🧩 第一步:语法定义解析(你的 BNF 文法规则) ### ✅ 开始符号 ```text <程序> → <变量说明部分> <语句部分> ``` 这是标准结构。相当于: ```c int x, y; x := 10; y := x + 5; ``` --- ### 🔤 变量说明部分 ```text <变量说明部分> → <变量说明部分> <变量说明语句>; | <变量说明语句>; <变量说明语句> → <变量说明> <标识符列表> <变量说明> → int | string | bool <标识符列表> → <标识符列表>, <标识符> | <标识符> <标识符> → $ <字母> <字母序列> $ ``` #### 解读: - 支持批量声明:`int x, y, z;` - 标识符格式为 `$abc$` 形式(有点特别,不是常规命名) - 数据类型:`int`, `string`, `bool` > ❗注意:你用了 `$...$` 包裹标识符,这与大多数语言不同(如 Pascal 风格),需要 Lexer 特别处理。 --- ### 📜 语句部分 ```text <语句部分> → <语句部分>; <语句> | <语句> <语句> → <赋值语句> | <条件语句> | <循环语句> <赋值语句> → <标识符> := <表达式> ``` 支持三种基本语句: 1. 赋值语句:`x := expr` 2. 条件语句:`if (expr) stmt else stmt` 3. 循环语句:`while (expr) : stmt` 还支持复合语句 `{ ... }` --- ### 📘 表达式结构(带优先级) 你的表达式是分层设计的,符合典型递归下降风格: | 层级 | 规则 | |------|------| | 最高 | `not` 一元逻辑非 | | ↓ | `and`(左结合) | | ↓ | `or`(左结合) | | ↓ | 关系运算 `<`, `>`, `==`, `<>`, `<=`, `>=` | | ↓ | 算术加减 `+`, `-`(左结合) | | 最低 | 算术乘除 `%`, `/`, `*`(左结合) | 括号 `( )` 可提升优先级。 ✅ 这是一个清晰、无歧义的表达式语法! --- ### 💡 特殊词法规则 ```text <字符串> → “<字母序列>” <整数数列> → <数字>+ <字母序列> → (<字母>|<数字>)* <字母> → a|b|...|z <数字> → 0|1|...|9 ``` > 注意:“整数数列”其实就是整数常量。 --- ## ✅ 总结:语法定义已完成 ✔️ | 项目 | 是否明确 | |------|----------| | 开始符号 `<程序>` | ✅ | | 变量声明语法 | ✅(支持多变量) | | 标识符格式 `$abc$` | ✅(需 Lexer 支持) | | 语句结构(赋值/条件/循环) | ✅ | | 嵌套语句和 `{}` 块 | ✅ | | 表达式分层(优先级正确) | ✅ | | 运算符完整覆盖 | ✅ | 👉 **结论:语法定义完整且可用!** --- ## 🧠 第二步:语义定义解析 现在来看你写的语义规则,非常详细! --- ### ✅ 1. 声明语句语义 > “可以定义 int, string, bool 的变量。” ✔️ 明确支持三种类型 ✔️ 应在符号表中记录:名称、类型、作用域 --- ### ✅ 2. 赋值语句语义 > “将整数赋给 int,字符串赋给 string,true/false 赋给 bool。必须类型检查。” ✔️ 强类型赋值 ❗但允许隐式转换吗?比如 `bool → int`?看下面有说明。 --- ### ✅ 3. 表达式语义(重点!复杂!) #### 🔹 逻辑运算(or, and, not) - 操作数:布尔型变量或值 - 结果:布尔值 - 优先级:`not > and > or`(右结合 or 左结合?默认左) ✔️ 正常逻辑运算 #### 🔹 关系运算(<, >, == 等) - 整数 vs 整数 → 布尔结果 - 字符串 vs 字符串 → 比较长度 → 布尔结果 - 布尔参与比较时:`true=1`, `false=0` ⚠️ 问题:不同类型能否比较?例如 `int == bool`?应禁止或强制转换? #### 🔹 算术运算(+, -, *, /, %) - 整数之间:正常计算 - 布尔参与:`true=1`, `false=0` - 减法结果不能小于 0 → 自动置为 0 - 乘法最多保留前 4 位(溢出截断?) - `/` 是整除,不能除 0 - `%` 取余,也不能除 0 ❗这是自定义行为!不是标准语义,要特别实现。 #### 🔹 字符串运算 - `"+"`:连接两个字符串字符串 - `"*"`:字符串 * 整数 → 重复该字符串 - 重复 0 次 → 空串 - 最多重复 2 次(限制最大次数)→ 强制约束! ❌ 其他组合均为错误(如 `int + string`) --- ### ✅ 4. 控制语句语义 #### `if (expr) stmt else stmt` - 条件表达式必须求值为布尔类型 - 分支选择执行 #### `while (expr) : stmt` - 判断 expr 是否为 true - 若是,则执行循环体;否则退出 ⚠️ 注意:你用了 `:` 而不是 `{}` 或换行,可能是语法糖。 --- ### ✅ 5. 复合语句 `{ <语句部分> }` - 支持局部作用域? - 是否支持嵌套块?理论上可以 --- ## ✅ 总结:语义定义也已完成 ✔️ | 语义特性 | 是否定义清楚 | |--------|---------------| | 类型系统(int/string/bool) | ✅ | | 赋值类型检查 | ✅ | | 表达式优先级与结合性 | ✅ | | 布尔参与算术/关系运算 | ✅(映射为 1/0) | | 字符串比较基于长度 | ✅ | | 字符串拼接与重复 | ✅(有限制) | | 算术特殊规则(去负、截断) | ✅(自定义) | | 除零检测 | ✅ | | 非法操作报错 | ✅ | 👉 **结论:语义规则完整,具备构建解释器或静态检查器的基础!** --- ## 🎯 当前状态评估:你满足了吗? | 维度 | 是否满足 | |------|----------| | ✅ 语法定义(BNF) | ✔️ 完整、清晰、可实现 | | ✅ 语义定义 | ✔️ 明确、细节丰富、边界条件覆盖 | | ✅ 类型系统 | ✔️ 支持三种基础类型 | | ✅ 表达式结构 | ✔️ 分层设计,支持优先级 | | ✅ 控制流 | ✔️ if/else, while 循环 | | ✅ 运算特异性 | ✔️ 如字符串重复限次、减法去负等 | 🟢 **答案:是的,你已经满足了一个小型教学语言的所有必要定义!** --- ## 🚀 接下来你可以做什么? 你现在处于一个绝佳位置:**从“设计语言”进入“实现编译器”阶段** ### 推荐下一步动作: ### ➤ 第一步:更新 Lexer —— 支持 `$abc$` 格式的标识符 你需要识别以 `$` 开头和结尾的标识符,例如 `$x$`, `$count$` ```csharp // 在 Lexer 中添加: if (currentChar == '$') { var sb = new StringBuilder(); sb.Append(Consume()); // 吃掉第一个 $ while (IsAlpha(currentChar) || IsDigit(currentChar)) sb.Append(Consume()); if (currentChar != '$') throw new SyntaxError("标识符未闭合:缺少结尾 $"); sb.Append(Consume()); // 吃掉结尾 $ AddToken(TokenType.Identifier, sb.ToString()); } ``` > 或者你可以简化:去掉 `$`,直接用 `x := 10;` 更通用(建议用于教学) --- ### ➤ 第二步:构建 AST 节点类 你需要这些类: ```csharp abstract class StatementNode { } class VarDeclStatement : StatementNode { } // int x,y; class AssignmentStatement : StatementNode { } // x := expr class IfStatement : StatementNode { } // if (...) ... else ... class WhileStatement : StatementNode { } // while (...) : ... class BlockStatement : StatementNode { } // { ... } abstract class ExpressionNode { public Type ResultType; } class BinaryExpr : ExpressionNode { } // a + b, x and y class UnaryExpr : ExpressionNode { } // not x class LiteralExpr : ExpressionNode { } // 123, "hello", true class VariableExpr : ExpressionNode { } // $x$ ``` --- ### ➤ 第三步:Parser 实现递归下降解析 按优先级写函数: ```csharp ExpressionNode ParseExpression() => ParseDisjunction(); ExpressionNode ParseDisjunction() => ParseLeftAssoc(ParseConjunction, "or"); ExpressionNode ParseConjunction() => ParseLeftAssoc(ParseInversion, "and"); ExpressionNode ParseInversion() => Current.Token == "not" ? ... : ParseRelational(); ExpressionNode ParseRelational() => ... ExpressionNode ParseArithmetic() => ParseAdditive(); ExpressionNode ParseAdditive() => ParseLeftAssoc(ParseTerm, "+", "-"); ExpressionNode ParseTerm() => ParseLeftAssoc(ParseFactor, "*", "/", "%"); ExpressionNode ParseFactor() => ... ``` --- ### ➤ 第四步:SemanticAnalyzer 添加类型推导 每种表达式都要设置 `.ResultType`: ```csharp // 示例:加法 if (op == "+") { if (left.Type == typeof(string) && right.Type == typeof(string)) expr.ResultType = typeof(string); else if (IsNumeric(left.Type) && IsNumeric(right.Type)) expr.ResultType = typeof(int); else throw TypeError("+ only supports str+str or num+num"); } ``` --- ## 🎉 总结:你现在完全可以开始编码了! 你已经有: ✅ 完整语法(BNF) ✅ 清晰语义(含所有运算规则) ✅ 明确类型系统 ✅ 特殊行为定义(如重复最多2次) 👉 **这就是一个完整的“迷你语言”设计方案!** --- ##
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值