私は逆文字列プログラムを開発しました。これを行うためのより良い方法があり、私のコードに潜在的な問題があるかどうか疑問に思っています。私は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;
}
}
}
Cの高度な機能を練習したい場合は、ポインターはどうですか?マクロを投げたり、xor-swapを楽しんだりすることもできます!
_#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
}
}
_
Apointer(eg _char *
_、右から左にpointer to char
として読み取る)は、別の値のメモリ内の場所を参照するために使用されるCのデータ型です。この場合、char
が格納されている場所。dereferenceポインターの前に_*
_を付けることで、その場所に格納されている値を取得できます。したがって、str
に格納される値は_*str
_です。
ポインタを使用して簡単な算術演算を行うことができます。ポインターをインクリメント(またはデクリメント)する場合、そのタイプの値の次の(または前の)メモリー位置を参照するようにポインターを移動するだけです。 Cでは異なる値のバイトサイズが異なるため、異なるタイプのポインタをインクリメントすると、異なるバイト数でポインタが移動する場合があります。
ここでは、1つのポインターを使用して文字列の最初の未処理のchar
(str
)を参照し、別のポインターを使用して最後の(end
)を参照します。値(_*str
_と_*end
_)を交換し、ポインターを文字列の中央に移動します。 _str >= end
_になったら、両方とも同じchar
を指します。これは、元の文字列の長さが奇数であること(および中央のchar
を逆にする必要がないこと)、またはすべてを処理したことを意味します。
スワッピングを行うために、macroを定義しました。マクロは、Cプリプロセッサによって実行されるテキスト置換です。それらは関数とは非常に異なり、違いを知ることが重要です。関数を呼び出すと、関数は指定した値のコピーに対して機能します。マクロを呼び出すと、単にテキストの置換が行われるだけです。そのため、指定した引数は直接使用されます。
_XOR_SWAP
_マクロは一度しか使用しなかったため、おそらく定義するのはやり過ぎだったかもしれませんが、何をしているのかがより明確になりました。 Cプリプロセッサがマクロを展開すると、whileループは次のようになります。
_ while (str < end)
{
do { *str ^= *end; *end ^= *str; *str ^= *end; } while (0);
str++;
end--;
}
_
マクロ引数は、マクロ定義で使用されるたびに1回表示されることに注意してください。これは非常に便利ですが、誤って使用するとコードが破損する可能性があります。たとえば、インクリメント/デクリメント命令とマクロ呼び出しを単一行に圧縮した場合、
_ XOR_SWAP(*str++, *end--);
_
次に、これは
_ do { *str++ ^= *end--; *end-- ^= *str++; *str++ ^= *end--; } while (0);
_
tripleインクリメント/デクリメント操作があり、実際に行うはずのスワップを行いません。
このテーマについては、xor(_^
_)の意味を知っておく必要があります。それは基本的な算術演算です-加算、減算、乗算、除算のようなものですが、通常小学校では教えられません。加算のように、2つの整数をビットごとに組み合わせますが、キャリーオーバーは気にしません。 _1^1 = 0
_、_1^0 = 1
_、_0^1 = 1
_、_0^0 = 0
_。
よく知られているトリックは、xorを使用して2つの値を交換することです。これは、xorの3つの基本プロパティ:_x ^ 0 = x
_、_x ^ x = 0
_、および_x ^ y = y ^ x
_がすべての値x
およびy
に対して機能するためです。したがって、最初に2つの値_va
_および_vb
_を格納する2つの変数a
およびb
があるとします。
//最初: // a == va // b == vb a ^ = b; //現在:a == va ^ vb b ^ = a; //現在:b == vb ^(va ^ vb) // == va ^(vb ^ vb) // == va ^ 0 // == va a ^ = b; //現在:a ==(va ^ vb)^ va // ==(va ^ va)^ vb // == 0 ^ vb // == vb
したがって、値は交換されます。これには1つのバグがあります-a
とb
が同じ変数の場合:
//最初: // a == va a ^ = a; //現在:a == va ^ va // == 0 a ^ = a; //現在:a == 0 ^ 0 // == 0 a ^ = a; //現在:a == 0 ^ 0 // == 0
_str < end
_なので、これは上記のコードでは発生しないため、大丈夫です。
正確性を懸念する一方で、Edgeのケースを確認する必要があります。 if (str)
行は、stringのNULL
ポインターが与えられていないことを確認する必要があります。空の文字列_""
_はどうですか? strlen("") == 0
であるため、end
を_str - 1
_として初期化します。つまり、while (str < end)
条件は決して真ではないため、何もしません。どちらが正しい。
探索するCの束があります。それを楽しんでください!
Update:mmw は良い点をもたらしますが、これを呼び出すには少し注意する必要がありますインプレースで動作します。
_ char stack_string[] = "This string is copied onto the stack.";
inplace_reverse(stack_string);
_
_stack_string
_は配列であり、その内容は指定された文字列定数に初期化されるため、これは正常に機能します。しかしながら
_ char * string_literal = "This string is part of the executable.";
inplace_reverse(string_literal);
_
実行時にコードが炎上し、消滅します。これは、_string_literal
_が実行可能ファイルの一部として保存されている文字列を指しているだけだからです。通常、この文字列はOSによる編集が許可されていないメモリです。より幸せな世界では、コンパイラはこれを知っており、コンパイルしようとしたときにエラーをせき、内容を変更できないので_string_literal
_の型は_char const *
_である必要があることを伝えます。しかし、これは私のコンパイラが住んでいる世界ではありません。
いくつかのメモリがスタック上またはヒープ内にある(したがって編集可能である)ことを確認しようとするハックがいくつかありますが、それらは必ずしも移植性があるわけではなく、かなり見苦しいかもしれません。ただし、これに対する責任を関数呼び出し元に任せてうれしいです。この関数はメモリ操作を適切に行うことを彼らに伝えました。それを可能にする引数を私に与えるのは彼らの責任です。
ただ再配置、および安全性チェック。また、使用されていない戻り型も削除しました。これは次のように安全でクリーンだと思います:
#include <stdio.h>
#include <string.h>
void reverse_string(char *str)
{
/* skip null */
if (str == 0)
{
return;
}
/* skip empty string */
if (*str == 0)
{
return;
}
/* get range */
char *start = str;
char *end = start + strlen(str) - 1; /* -1 for \0 */
char temp;
/* reverse */
while (end > start)
{
/* swap */
temp = *start;
*start = *end;
*end = temp;
/* move */
++start;
--end;
}
}
int main(void)
{
char s1[] = "Reverse me!";
char s2[] = "abc";
char s3[] = "ab";
char s4[] = "a";
char s5[] = "";
reverse_string(0);
reverse_string(s1);
reverse_string(s2);
reverse_string(s3);
reverse_string(s4);
reverse_string(s5);
printf("%s\n", s1);
printf("%s\n", s2);
printf("%s\n", s3);
printf("%s\n", s4);
printf("%s\n", s5);
return 0;
}
Strlenが0の場合、endが不良メモリ位置を指し示しないように編集されました。
(len/2)
forループでテストします。
for(i = 0,k=len-1 ; i < (len/2); i++,k--)
{
temp = str[k];
str[k] = str[i];
str[i] = temp;
}
この完全なプログラムは、私がそれをどのように行うかを示しています。あなたのほとんどがあなたの母親の目にキラキラ光っていたとき、私はCを書いていたので覚えておいてください、それはそれが古い学校で、仕事をして、長いvar-names-are-for-wimpsです。必要に応じて修正し、コードの正確性に興味があります。
NULL、空の文字列、すべての文字列サイズを処理します。私は最大サイズの文字列(max(size_t))でテストしていませんが、動作するはずです。もしあなたがそのような大きな文字列を処理しているなら、とにかく正気です:-)
#include <stdio.h>
#include <string.h>
char *revStr (char *str) {
char tmp, *src, *dst;
size_t len;
if (str != NULL)
{
len = strlen (str);
if (len > 1) {
src = str;
dst = src + len - 1;
while (src < dst) {
tmp = *src;
*src++ = *dst;
*dst-- = tmp;
}
}
}
return str;
}
char *str[] = {"", "a", "ab", "abc", "abcd", "abcde"};
int main(int argc, char *argv[]) {
int i;
char s[10000];
for (i=0; i < sizeof(str)/sizeof(str[0]); i++) {
strcpy (s, str[i]);
printf ("'%s' -> '%s'\n", str[i], revStr(s));
}
return 0;
}
その出力は次のとおりです。
'' -> ''
'a' -> 'a'
'ab' -> 'ba'
'abc' -> 'cba'
'abcd' -> 'dcba'
'abcde' -> 'edcba'
これを試して:
reverse_string(NULL);
reverse_string("");
誰もポインターを使用しなくなりましたか?
void inplace_rev( char * s ) {
char t, *e = s + strlen(s);
while ( --e > s ) { t = *s;*s++=*e;*e=t; }
}
編集:申し訳ありませんが、上記のXOR例...
Forループ宣言を変更して、コードを短くすることができます。
char* reverse_string(char *str)
{
char temp;
size_t len = strlen(str) - 1;
size_t stop = len/2;
size_t i,k;
for(i = 0, k = len; i < stop; i++, k--)
{
temp = str[k];
str[k] = str[i];
str[i] = temp;
}
return str;
}
Returnステートメントが表示されず、入力文字列を変更しているため、プログラマーにとって問題になる可能性があります。入力文字列を不変にすることができます。
また、これは難しいかもしれませんが、len/2はIMOで1回だけ計算する必要があります。
それ以外は、rossfabricantが言及した問題のケースを処理する限り、機能します。
void reverse(char *s)
{
char *end,temp;
end = s;
while(*end != '\0'){
end++;
}
end--; //end points to last letter now
for(;s<end;s++,end--){
temp = *end;
*end = *s;
*s = temp;
}
}
rev {
int len = strlen(str)-1;
for ( int i =0; i< len/2 ; i++ ) {
char t = str[i];
str[i] = str[len-i];
str[len-i] = t;
}
}
bool reverse_string(char* str)
{
// Make sure str is reversible
if (!str || strlen(str) < 2)
return false;
char* first = str;
char* last = str + strlen(str) - 1; // Minus 1 accounts for Index offset
char temp;
do{
temp = *first;
*first = *last;
*last = temp;
}
while (++first < --last); // Update Pointer Addresses and check for equality
return true;
}
このソリューションは、いくつかの変更を加えたGManNickGの投稿に基づいています。 strlen操作の前に!strが評価されない場合、最初の論理ステートメントは危険です(NULL ptrの場合)。これは私のコンパイラには当てはまりません。 do-whileループの素敵な例だから、このコードを追加すると思った。
私の2セント:
_/* Reverses n characters of a string and adds a '\0' at the end */
void strnrev (char *txt, size_t len) {
size_t idx;
for (idx = len >> 1; idx > 0; idx--) {
txt[len] = txt[idx - 1];
txt[idx - 1] = txt[len - idx];
txt[len - idx] = txt[len];
}
txt[len] = '\0';
}
/* Reverses a null-terminated string */
void strrev (char *txt) {
size_t len = 0;
while (txt[len++]);
strnrev(txt, --len);
}
_
テスト#1 – strrev()
:
_char string[] = "Hello world!";
strrev(string);
printf("%s\n", string); // Displays "!dlrow olleH"
_
テスト#2 – strnrev()
:
_char string[] = "Hello world!";
strnrev(string, 5);
printf("%s\n", string); // Displays "olleH"
_
簡単でシンプルなコードxD
void strrev (char s[]) {
int i;
int dim = strlen (s);
char l;
for (i = 0; i < dim / 2; i++) {
l = s[i];
s[i] = s[dim-i-1];
s[dim-i-1] = l;
}
}
あなたは空想を得たいと言うので、おそらくあなたは XORスワップ を使用してキャラクターを交換したいと思うでしょう。
途中で中断するのではなく、単にループを短くする必要があります。
size_t length = strlen(str);
size_t i;
for (i = 0; i < (length / 2); i++)
{
char temp = str[length - i - 1];
str[length - i - 1] = str[i];
str[i] = temp;
}
Here is my shot which will handle all the cases
char *p ="KDLAKDADKADAD"
char p[] = "lammdlamldaldladadada"
also empty string
#include<stdio.h>
#include<string.h>enter code here
#include<stdlib.h>
char *string_reverse(char *p);
int main()
{
char *p = " Deepak@klkaldkaldkakdoroorerr";
char *temp = string_reverse(p);
printf("%s", temp);
}
char * string_reverse( char *p )
{
if(*p == '\0')
{
printf("No charecters are present \n");
return 0;
}
int count = strlen(p)+1;
int mid = strlen(p)/2;
char *q = (char *)malloc(count * sizeof(char));
if( q )
{
strcpy(q,p);
char *begin,*end,temp;
begin = q ;
end = q+strlen(p)-1 ;
int i = 0;
while( i < mid/2 )
{
temp = *end;
*end = *begin;
*begin = temp;
begin++;
end--;
i++;
}
return q;
}
else
{
printf("Memory Not allocated ");
}
free(q);
}
#include <stdio.h>
int main()
{
char string[100];
int i;
printf("Enter a string:\n");
gets(string);
printf("\n");
for(i=strlen(string)-1;i>-1;i--)
printf("%c",string[i]);
}
コードは不必要に複雑に見えます。これが私のバージョンです:
void strrev(char* str) {
size_t len = strlen(str);
char buf[len];
for (size_t i = 0; i < len; i++) {
buf[i] = str[len - 1 - i];
};
for (size_t i = 0; i < len; i++) {
str[i] = buf[i];
}
}
このポインター演算を試すことができます:
void revString(char *s)
{
char *e = s; while(*e){ e++; } e--;
while(e > s){ *s ^= *e; *e ^= *s; *s++ ^= *e--; }
}
それはいい質問です ant2009 。スタンドアロン関数を使用して、文字列を反転できます。コードは...
#include <stdio.h>
#define MAX_CHARACTERS 99
int main( void );
int strlen( char __str );
int main() {
char *str[ MAX_CHARACTERS ];
char *new_string[ MAX_CHARACTERS ];
int i, j;
printf( "enter string: " );
gets( *str );
for( i = 0; j = ( strlen( *str ) - 1 ); i < strlen( *str ), j > -1; i++, j-- ) {
*str[ i ] = *new_string[ j ];
}
printf( "Reverse string is: %s", *new_string" );
return ( 0 );
}
int strlen( char __str[] ) {
int count;
for( int i = 0; __str[ i ] != '\0'; i++ ) {
++count;
}
return ( count );
}
これが私のショットです。標準のstrcpy
パターンを使用するだけでスワップを回避します。
char *string_reverse(char *dst, const char *src)
{
if (src == NULL) return NULL;
const char *src_start = src;
char *dst_end = dst + strlen(src);
*dst_end = '\0';
while ((*--dst_end = *src_start++)) { ; }
return dst;
}
#include <stdio.h>
#include <string.h>
int main()
{
char *data = "hello world";
int length=strlen(data);
char bytes[length];
int n=0;
while(n<=length)
{
bytes[n] = data[length-n-1];
n++;
}
printf("%s\n", bytes);
return 0;
}
/* Author: Siken Dongol */
#include <stdio.h>
int strLength(char *input) {
int i = 0;
while(input[i++]!='\0');
return --i;
}
int main()
{
char input[] = "Siken Man Singh Dongol";
int len = strLength(input);
char output[len];
int index = 0;
while(len >= 0) {
output[index++] = input[--len];
}
printf("%s\n",input);
printf("%s\n",output);
return 0;
}