扭转在C字符串 [英] Reversing a string in C

查看:76
本文介绍了扭转在C字符串的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我已经开发出一种反向串程序。我想知道,如果有更好的方式来做到这一点,如果我的code有任何潜在的问题。我期待C.练习的一些高级特性

 的char * reverse_string(字符*海峡)
{
焦温度;
size_t型LEN = strlen的(STR) - 1;
为size_t我;
为size_t K = LEN;对于(i = 0; I< LEN,我++)
{
TEMP = STR [K];
海峡[K] = STR [I]
海峡[I] =温度;
K--;/ *由于2字符为循环的每个周期发生变化
仅遍历字符数组一半* /
如果(K ==(LEN / 2))
{
突破;
}
    }
}


解决方案

如果你想练的C先进的功能,怎么样指针?
我们可以抛在宏和异或交换的乐趣呢!

 的#include<&string.h中GT; //函数strlen()//反转的地方给定的空终止字符串
无效inplace_reverse(字符*海峡)
{
  如果(STR)
  {
    字符*结束= STR +的strlen(STR) - 1;    //交换两个给定的变量值
    // XXX:当a和b指向同一个内存位置失败
#定义XOR_SWAP(A,B)做\\
    {\\
      一个^ = B; \\
      b ^ =一; \\
      一个^ = B; \\
    }而(0)    //从字符串的两端向内走,
    //交换,直到我们得到的中间
    而(STR<结束)
    {
      XOR_SWAP(*海峡,*结束);
      海峡++;
      结束 - ;
    }
#民主基金XOR_SWAP
  }
}

A 指针(如的char * ,读取从右到左为的指针字符 的)是用于在C数据类型
参考位置在另一个值的存储器。在这种情况下,
字符的存储位置。我们可以的解引用
由$ P $指针与pfixing他们 * ,这让我们的价值
存储在该位置。所以保存在 STR 值为 * STR

我们可以做简单的算术与指针。当我们增加(或减少)
一个指针,我们只需将其移动到参考下(或previous)
存储器位置为该类型的值。递增的指针
不同类型的可通过不同数目的移动指针
字节,因为不同的价值观必须用C不同的字节大小。

下面,我们用一个指针指到第一未处理
字符的字符串( STR ),另一个指的是最后一个(结束)。
我们交换它们的值( * STR *结束),并移动指针
向内的字符串的中间位置。一旦 STR> =结束,无论是
它们都指向同一个字符,这意味着我们的原始字符串有一个
奇数长度(及中间的字符并不需要反转),或
我们处理一切。

要做到交换,我已经定义了一个即可。宏是文本替换
由C preprocessor完成。他们是从功能非常不同,
并知道其中的差别是很重要的。当你调用一个函数,
在功能上,你给它的值的副本进行操作。当您打电话
一个宏,它只是做了文本替换 - 所以你给的参数
它被直接使用。

由于我只使用了 XOR_SWAP 宏观一次,它可能是矫枉过正来定义它,
但它变得更清楚我在做什么。之后的C preprocessor宏扩展,
while循环是这样的:

 而(STR<结束)
    {
      做{* ^海峡= *结束; * ^结束= *海峡; * ^海峡= *结束; }而(0);
      海峡++;
      结束 - ;
    }

请注意,该宏参数显示一旦他们在每次使用时间
宏定义。这是非常有用的 - 而且还可以打破你的code
如果使用不当。举例来说,如果我有COM pressed递增/递减
指令和宏调用成一条线,像

  XOR_SWAP(*海峡++,* end--);

那么这将扩大到

 做{*海峡++ ^ = * end--; * ^ end-- = *海峡++; * STR + ^ = * end--; }而(0);

其中具有的的递增/递减操作,实际上并不
这样做应该做掉。

虽然我们对这个问题,你应该知道的 XOR ^ )的意思。这是一个基本的
算术运算 - 像加,减,乘,除,除
它不是通常教小学。它逐位组合两个整数位
- 像另外的,但我们不关心结转。 1 ^ 1 = 0 1 ^ 0 = 1
0 ^ 1 = 1 0 ^ 0 = 0

一个众所周知的技巧是使用异或交换​​两个值。这工作,因为三个基本
XOR的性质: X ^ 0 = X X ^ X = 0 X ^ Y = Y ^ X 所有值 X 。所以说,我们有两个
变量 A B 的最初存放两个值
v A v <子>乙


  // 原来:
  //一个== v A
  // B == v <子>乙
  一个^ = B;
  //现在:一个== v A ^ V <子>乙
  b ^ =一;
  //现在:乙== v <子>乙 ^(V A ^ V <子>乙)
  // == v A ^(V <子>乙 ^ V <子>乙)
  // == v A ^ 0
  // == v A
  一个^ = B;
  //现在:一个==(V A ^ V <子>乙)^ V A
  // ==(V A ^ V A )^ V <子>乙
  // == 0 ^ V <子>乙
  // == v <子>乙

因此​​,值交换。这确实有一个错误 - 当 A B 是相同的变量:


  // 原来:
  //一个== v A
  一个^ =一;
  //现在:一个== v A ^ V A
  // == 0
  一个^ =一;
  //现在:一个== 0 ^ 0
  // == 0
  一个^ =一;
  //现在:一个== 0 ^ 0
  // == 0

由于我们 STR&LT;结束,这永远不会发生在上述code,所以我们没事。

虽然我们关注的正确性,我们要检查我们的边缘情况。在如果(STR)行应该确保我们没有给出字符串 NULL 指针。什么空字符串?那么的strlen()== 0 ,所以我们会初始化结束海峡 - 1 ,这意味着而(STR&LT;结束)的条件是不正确的,所以我们没有做任何事情。这是正确的。

有一堆的C探索。有它的乐趣!

更新: MMW 带来了一个好点,这是你必须要稍微细心的你如何调用这个,因为它在的地方进行操作。

 字符stack_string [] =此字符串复制到堆栈中。
 inplace_reverse(stack_string);

这工作得很好,因为 stack_string 是一个数组,其内容被初始化为给定的字符串常量。然而

 的char * string_literal =此字符串是可执行文件的一部分。
 inplace_reverse(string_literal);

将导致您的code火焰并在运行时死去。这是因为 string_literal 仅仅指向存储为你的可执行文件的一部分字符串 - 通常是你不能由操作系统来编辑内存。在一个快乐的世界,你的编译器会知道这一点,咳嗽一个错误,当您试图编译,告诉你, string_literal 必须是类型字符常量* ,因为你不能修改其内容。然而,这不是世界我的编译器住在

有一些黑客,你可以尝试以确保一些内存是在栈或堆(因此编辑),但他们不一定便携,它可能是pretty难看。不过,我更乐意扔到函数调用了这个责任。我告诉他们,在地方存储器操作这个函数,这是他们的责任,给我一个说法,它允许。

I have developed a reverse-string program. I am wondering if there is a better way to do this, and if my code has any potential problems. I am looking to practice some advanced features of C.

char* reverse_string(char *str)
{
char temp;
size_t len = strlen(str) - 1;
size_t i;
size_t k = len;

for(i = 0; i < len; i++)
{
	temp = str[k];
	str[k] = str[i];
	str[i] = temp;
	k--;

	    /* As 2 characters are changing place for each cycle of the loop
	       only traverse half the array of characters */
	    if(k == (len / 2))
	    {
	   	    break;
	    }
    }
}

解决方案

If you want to practice advanced features of C, how about pointers? We can toss in macros and xor-swap for fun too!

#include <string.h> // for strlen()

// reverse the given null-terminated string in place
void inplace_reverse(char * str)
{
  if (str)
  {
    char * end = str + strlen(str) - 1;

    // swap the values in the two given variables
    // XXX: fails when a and b refer to same memory location
#   define XOR_SWAP(a,b) do\
    {\
      a ^= b;\
      b ^= a;\
      a ^= b;\
    } while (0)

    // walk inwards from both ends of the string, 
    // swapping until we get to the middle
    while (str < end)
    {
      XOR_SWAP(*str, *end);
      str++;
      end--;
    }
#   undef XOR_SWAP
  }
}

A pointer (e.g. char *, read from right-to-left as a pointer to a char) is a data type in C that is used to refer to location in memory of another value. In this case, the location where a char is stored. We can dereference pointers by prefixing them with an *, which gives us the value stored at that location. So the value stored at str is *str.

We can do simple arithmetic with pointers. When we increment (or decrement) a pointer, we simply move it to refer to the next (or previous) memory location for that type of value. Incrementing pointers of different types may move the pointer by a different number of bytes because different values have different byte sizes in C.

Here, we use one pointer to refer to the first unprocessed char of the string (str) and another to refer to the last (end). We swap their values (*str and *end), and move the pointers inwards to the middle of the string. Once str >= end, either they both point to the same char, which means our original string had an odd length (and the middle char doesn't need to be reversed), or we've processed everything.

To do the swapping, I've defined a macro. Macros are text substitution done by the C preprocessor. They are very different from functions, and it's important to know the difference. When you call a function, the function operates on a copy of the values you give it. When you call a macro, it simply does a textual substitution - so the arguments you give it are used directly.

Since I only used the XOR_SWAP macro once, it was probably overkill to define it, but it made more clear what I was doing. After the C preprocessor expands the macro, the while loop looks like this:

    while (str < end)
    {
      do { *str ^= *end; *end ^= *str; *str ^= *end; } while (0);
      str++;
      end--;
    }

Note that the macro arguments show up once for each time they're used in the macro definition. This can be very useful - but can also break your code if used incorrectly. For example, if I had compressed the increment/decrement instructions and the macro call into a single line, like

      XOR_SWAP(*str++, *end--);

Then this would expand to

      do { *str++ ^= *end--; *end-- ^= *str++; *str++ ^= *end--; } while (0);

Which has triple the increment/decrement operations, and doesn't actually do the swap it's supposed to do.

While we're on the subject, you should know what xor (^) means. It's a basic arithmetic operation - like addition, subtraction, multiplication, division, except it's not usually taught in elementary school. It combines two integers bit by bit - like addition, but we don't care about the carry-overs. 1^1 = 0, 1^0 = 1, 0^1 = 1, 0^0 = 0.

A well known trick is to use xor to swap two values. This works because of three basic properties of xor: x ^ 0 = x, x ^ x = 0 and x ^ y = y ^ x for all values x and y. So say we have two variables a and b that are initially storing two values va and vb.

  // initially:
  // a == va
  // b == vb
  a ^= b;
  // now: a == va ^ vb
  b ^= a;
  // now: b == vb ^ (va ^ vb)
  //        == va ^ (vb ^ vb)
  //        == va ^ 0
  //        == va
  a ^= b;
  // now: a == (va ^ vb) ^ va
  //        == (va ^ va) ^ vb
  //        == 0 ^ vb
  //        == vb

So the values are swapped. This does have one bug - when a and b are the same variable:

  // initially:
  // a == va
  a ^= a;
  // now: a == va ^ va
  //        == 0
  a ^= a;
  // now: a == 0 ^ 0
  //        == 0
  a ^= a;
  // now: a == 0 ^ 0
  //        == 0

Since we str < end, this never happens in the above code, so we're okay.

While we're concerned about correctness we should check our edge cases. The if (str) line should make sure we weren't given a NULL pointer for string. What about the empty string ""? Well strlen("") == 0, so we'll initialize end as str - 1, which means that the while (str < end) condition is never true, so we don't do anything. Which is correct.

There's a bunch of C to explore. Have fun with it!

Update: mmw brings up a good point, which is you do have to be slightly careful how you invoke this, as it does operate in-place.

 char stack_string[] = "This string is copied onto the stack.";
 inplace_reverse(stack_string);

This works fine, since stack_string is an array, whose contents are initialized to the given string constant. However

 char * string_literal = "This string is part of the executable.";
 inplace_reverse(string_literal);

Will cause your code to flame and die at runtime. That's because string_literal merely points to the string that is stored as part of your executable - which is normally memory that you are not allowed to edit by the OS. In a happier world, your compiler would know this, and cough an error when you tried to compile, telling you that string_literal needs to be of type char const * since you can't modify the contents. However, this is not the world my compiler lives in.

There are some hacks you could try to make sure that some memory is on the stack or in the heap (and is therefore editable), but they're not necessarily portable, and it could be pretty ugly. However, I'm more than happy to throw responsibility for this to the function invoker. I've told them that this function does in place memory manipulation, it's their responsibility to give me an argument that allows that.

这篇关于扭转在C字符串的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆