Padding zeros in C - c

I have to add zero to the shorter bit string, but if this doesn't work for
str1 = 0011
str2 = 0101111100011
but it works if
str1 = 0101111100011
str2 = 0011
void addLeadingZero(char* str1 , char* str2) {
int len1 = strlen(str1);
int len2 = strlen(str2);
int diff = (len1 > len2) ? len1 : len2;
while (len1 < diff) {
for (int i = len1; i >= 0; i--) {
str1[i + 1] = str1[i]; // expend the shorter array length.
//until there is no difference in lengths.
}
str1[0] = '0'; //add zeros to it.
len1++;
}// loop stops when max diff is reached.
while (len2 < diff) { //same concept as above.
for (int i = len2; i >= 0; i--) {
str2[i + 1] = str2[i];
}
str2[0] = '0';
len2++;
}
}
output :
0000000000011
011
expected output
0000000000011
0101111100011

Your diff does not show the difference.
You can use memmove and memset for this task:
void addLeadingZero(char* str1 , char* str2)
{
size_t len1 = strlen(str1);
size_t len2 = strlen(str2);
char *wrk = len1 > len2 ? str2 : str1;
size_t diff = len1 > len2 ? (len1 - len2) : (len2 - len1);
if(diff)
{
memmove(wrk + diff, wrk, len1 > len2 ? len2 + 1 : len1 + 1);
memset(wrk, '0', diff);
}
}
Remember that str1 & str2 have to be modifiable and large enough to accommodate the padded string.

In order to pad a string, the containing buffer must have enough space for the padding and the original string, and it must be modifiable. Otherwise, Undefined Behaviour will be the result.
String literals are not modifiable, and should not be passed to such a function.
Arrays which have their size determined by the initialization (e.g., char foo[] = "bar";) will not contain any additional room for padding.
Dynamically allocated buffers could be resized with realloc to create space, or a new buffer could be allocated for the result.
For a different method, using standard library functions:
Use memmove to move the existing string towards the end of the buffer.
Use memset to fill the gap.
A simple example where the buffers are explicitly sized to have enough room:
#include <stdio.h>
#include <string.h>
void pad(char *string, size_t length, size_t new_length)
{
size_t diff = new_length - length;
memmove(string + diff, string, length + 1);
memset(string, '0', diff);
}
int main(void)
{
char a[64] = "123";
char b[64] = "456789";
size_t a_len = strlen(a);
size_t b_len = strlen(b);
if (a_len > b_len)
pad(b, b_len, a_len);
else if (b_len > a_len)
pad(a, a_len, b_len);
puts(a);
puts(b);
}
Output:
000123
456789

Related

How can I compare two strings by more than just one character?

So I want to compare two strings by more than just one character. I know how to compare two strings character by character, or just by character on an selected position but I'm not sure how to compare two string by more than one character? For example I want to compare two string by 3 last characters, how can I do that?
I've tried this:
if( strcmp(str1-1, str2-1)==0 && strcmp(str1-2, str2-2) ==0)
but it doesn't work. I'm taking two strings from the user with scanf function.
if( strcmp(str1, str2) ==0){
printf("equal");
}else{
printf("not");
You need to get the length of the strings, subtract 3 from that, and add that to the start of the string to get the index to start comparing. Also, make sure that you don't get a negative index if the length of the string is less than 3.
size_t len1 = strlen(str1);
size_t index1 = len1 >= 3 ? len1 - 3 : 0;
size_t len2 = strlen(str2);
size_t index2 = len2 >= 3 ? len2 - 3 : 0;
if (strcmp(str1 + index1, str2 + index2) == 0) {
printf("equal\n");
} else {
printf("not equal\n");
}
There is not such function in the standard, but you can roll your own, something like:
#include <stdio.h>
#include <string.h>
int revcmp(const char *a, const char *b, size_t start)
{
size_t len;
len = strlen(a);
if (len < start) {
return 0;
}
a += len - start;
len = strlen(b);
if (len < start) {
return 0;
}
b += len - start;
return !strcmp(a, b);
}
int main(void)
{
char *a = "abcxyz";
char *b = "123xyz";
printf("%d\n", revcmp(a, b, 3));
return 0;
}
First you need to determine the string length of both strings.
The lengths must be bigger than the number of last characters you want compare.
With pointer arithmetic you can move to the positions where you want start your comparison:
int strlen1 = strlen(str1);
int strlen2 = strlen(str2);
const int LASTCMP = 3;
if (strlen1 >= LASTCMP && strlen2 >= LASTCMP
&& strcmp(str1+strlen1-LASTCMP , str2+strlen2-LASTCMP ) == 0)
{
printf("last %d characters are equal", LASTCMP);
}
else
{
printf("not");
}
If you want to compare just certain number of chars, you can use strncmp.
If you want to compare last 3 characters, then you need to determine the index of 3rd last character of each strings, so you can start comparing from that. For that you need strlen.
Putting above together, and accounting for strings shorter than 3 chars:
int pos1 = strlen(str1) - 3;
int pos2 = strlen(str2) - 3;
if (pos1 >= 0 && pos2 >= 0 && strncmp(str1 + pos1, str2 + pos2, 3) == 0) {
// last 3 chars of the strings match
}

How to Append a character to a string literal in C

So I have seen that some people do not recommend using String literals to create a char in C. However, I have found this to be the most useful method and I have a char set as
char a[] = "456";
I understand that the memory is fixed in this data type. However, in my assignment I believe that I need to be able to reallocate memory for my char value since I am adding integers that are represented as strings. For example:
char b[] = "500";
char c[] = "501";
//add function for adding b and c. have a char d[] equal to "1000".
I am using long addition to accomplish this. I have tried experimenting with the realloc function but it did not work. How would I continuously append new characters onto a string?
For your assignment, you must define a destination array with enough space to hold the result, which for an addition is easy to determine: it is the length of the longest argument plus one character for a possible extra digit plus one character for the null terminator.
You can allocate this array with malloc() and return a pointer.
Here is an example:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
char *bigadd(const char *a, const char *b) {
size_t alen = strlen(a);
size_t blen = strlen(b);
size_t clen = (alen > blen) ? alen : blen;
size_t mlen = (alen > blen) ? blen : alen;
char *c = malloc(clen + 2);
if (c != NULL) {
size_t i;
int carry = 0;
c[clen] = '\0';
for (i = 1; i <= mlen; i++) {
carry += a[alen - i] - '0' + b[blen - i] - '0';
c[clen - i] = '0' + carry % 10;
carry /= 10;
}
for (; i <= alen; i++) {
carry += a[alen - i] - '0';
c[clen - i] = '0' + carry % 10;
carry /= 10;
}
for (; i <= blen; i++) {
carry += b[blen - i] - '0';
c[clen - i] = '0' + carry % 10;
carry /= 10;
}
if (carry) {
memmove(c + 1, c, clen + 1);
c[0] = (char)('0' + carry);
}
}
return c;
}
int main(int argc, char *argv[]) {
const char *a = argc > 1 ? argv[1] : "123456890123456890123456890";
const char *b = argc > 2 ? argv[2] : "2035864230956204598237409822324";
char *c = bigadd(a, b);
printf("%s + %s = %s\n", a, b, c);
free(c);
return 0;
}
so the way I see it is by typing:
char a[] = "456";
you initialise a character array of size 4 (3 chars +'\0'). storing anything longer in the same array could cause an undefined behavior and must be avoided. long story short: you can modify the value of this array, as long as the size of it doesn't change. You can initialise an array like this:
char a[100] = "456";
to leave some extra space for any possible additions to the string, but that's about it, since a size of the array is known at compiling, and doesn't ever change.
also please note that this won't work:
char * a = "456";
it's a read-only string literal which cannot be modified or realloced.
PS: I'm a novice, please correct me if I'm wrong!

Segfault 11 on long string, PRIOR to accessing string, only when string > 14

ANSI c on OSX 10.13.6
Apple LLVM version 9.1.0 (clang-902.0.39.2)
Target: x86_64-apple-darwin17.7.0
Thread model: posix
I'm learning c
This is a function that manually (character-by-character) adds two character strings representing large numbers (that exceed the unsigned long long or double size).
It functions fine with any two strings 14 or less characters long, but segmentation fault 11 with any strings greater than 14 chars.
Changing the string's memory allocation method seems to have no effect (I.e. from char[15] addend1; // not a ptr to char *addend1 = (char *) malloc(sizeof(char) * (16) ); // pointer
One things that's curious, is that it seems to segfault on the ...
for (int j = maxlength - 1 ; j >= 0; j--)
... prior to accessing either of addend1 or addend2, but I'm not able to find an error there or change it to prevent the segfault.
Am I misreading where the error arises, or could it be related to the for loop?
Successful run (less than 15 chars)
maxlength = 14
char *sum = (char *) malloc(sizeof(char) * (maxlength + 1) ) ... DONE
for (int i = 0; i < (maxlength); i++) { sum[i] = '0'; } ... DONE
Start adding individual ints from end (right side) ...
13 ...12 ...11 ...10 ...9 ...8 ...7 ...6 ...5 ...4 ...3 ...2 ...1 ...0 ...main.sum = 28147497671064
UNSuccessful run (15 chars)
maxlength = 15
char *sum = (char *) malloc(sizeof(char) * (maxlength + 1) ) ... DONE
for (int i = 0; i < (maxlength); i++) { sum[i] = '0'; } ... DONE
Start adding individual ints from end (right side) ...
Segmentation fault: 11
MAIN.c
#include <stdio.h>
#include <stdlib.h>
#include "../../c-library/include/addViaStrings.h"
int main(void) {
// s[0] = 72; s[1] = 101; s[2] = 108; s[3] = 108; s[4] = 111; s[5] = 32; s[6] = 87; s[7] = 111; s[8] = 114; s[9] = 108; s[10] = 100; s[11] = 0;
// WORKS
// char s1[] = "14073748835532";
// char s2[] = "14073748835532";
// FAILS
char s1[] = "140737488355328";
char s2[] = "140737488355328";
char *sum = addNumericStrings(&s1, &s2);
printf("main.sum = %s\n", sum);
}
addViaStrings.h
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
char* addNumericStrings(char *s1, char *s2);
char leftPad(char *result, char *s, int maxlength);
int findMaxLength(char *s1, char *s2);
char* addNumericStrings(char *s1, char *s2){
// Find the length of the greater of the two
int maxlength = findMaxLength(s1, s2);
printf("maxlength = %d\n", maxlength); //333
///////////////////////////////////////////////
// Using malloc instead of char[maxlength] seems to have NO EFFECT on the issue
// char addend1[maxlength]; // not a pointer
char *addend1 = (char *) malloc(sizeof(char) * (maxlength + 1) );
addend1[maxlength + 1] = 0; // end flag
// char addend2[maxlength]; // not a pointer
char *addend2 = (char *) malloc(sizeof(char) * (maxlength + 1) );
addend2[maxlength + 1] = 0; // end flag
// Allocate sum pointer
printf("char *sum = (char *) malloc(sizeof(char) * (maxlength + 1) ) ... "); //333
char *sum = (char *) malloc(sizeof(char) * (maxlength + 1) );
printf("DONE\n"); //333
// General use vars
int a1, a2, total;
int carry = 0;
// Prepare the strings for manual addition. Pad the left with char 0s
leftPad(addend1, s1, maxlength);
leftPad(addend2, s2, maxlength);
// Buffer sum with zeros
sum[maxlength + 1] = 0; // end flag
printf("for (int i = 0; i < (maxlength); i++) { sum[i] = '0'; } ... "); //333
for (int i = 0; i < (maxlength); i++) { sum[i] = '0'; } // Fill w/ 0s
printf("DONE\n"); //333
// Run the manual addition
// Start adding individual ints from end (right side)
printf("Start adding individual ints from end (right side) ...\n"); //333
// maxlength -1 because(I think) the termination char takes 2 bytes
// If I use (maxlength) instead of (maxlength -1) I get a weird
// question mark char at the end of returnsum
for (int j = maxlength - 1 ; j >= 0; j--) {
///////////////////////////////////////////
// The segfault seems to happen BEFORE accessing addend1 or addend2
printf("%d ...", j); // 333 This DOES NOT print
///////////////////////////////////////////
a1 = addend1[j] - '0'; // Convert to int
a2 = addend2[j] - '0'; // Convert to int
total = (a1 + a2 + carry);
carry = 0;
if ( total >= 10){
carry += 1;
total -= 10;
}
sum[j + 1] = '0'+total; // convert to ascii value for numbers (adding 48)
}
sum[0] = '0' + carry; // add last carry to start of num always, even if 0
// Before returning, truncate leading zeros
char *returnsum = (char *) malloc(sizeof(char) * (strlen(sum) + 1) );
int sum_i = 0;
int returnsm_i = 0;
// bool truncate = true; // Find out why this wont compile
int truncate = 1; // true
while (1){
// if order is important here
if (sum[sum_i] == '\0') { break; } // we're done
if (sum[sum_i] == '0' && truncate == 1) { sum_i += 1; continue; } // 1 is true
// if a num, Stop truncating 0s but DO continue adding numbers
if (sum[sum_i] != '0') { truncate = 0; } // 0 is false
returnsum[returnsm_i] = sum[sum_i];
returnsm_i += 1;
sum_i += 1;
}
return returnsum;
}
char leftPad(char *result, char *s, int maxlength){
int slength = strlen(s);
// buffer with zeros, not '\0's
for (int i = (maxlength); i >= 0; i--){ result[i] = '0'; }
// right fill result with s
for (int j = 0; j <= slength; j++){
int index = ((maxlength - slength) + j);
result[index] = s[j];
}
result[maxlength + 1] = 0;
}
int findMaxLength(char *s1, char *s2){
// int length1 = findEndLength(s1);
// int length2 = findEndLength(s2);
int length1 = strlen(s1);
int length2 = strlen(s2);
int maxlength;
(length1 > length2) ? (maxlength = length1) : (maxlength = length2);
return maxlength;
}
The issue was I was trying to access the sum string as if it was one position longer than the addends strings, but I had declared it as the same length (I.e. maxlength + 1). So I was attempting to access one position past the actual sum array.
This was a somewhat hidden problem, because it was not until the length of sum needed to be greater than 15, that this access error stepped into disallowed memory space, resulting in a segfault.
Details
Because the sum of two addends could conceivably require at least one additional position in the array if the sums carried over a one (I.e. 999 + 999 = 1998), the sum string needs to be one array position longer than the addends.
If the addends were 3 digits long (length of array = 4) then the sum needed to be 4 digits long (array length = 5).
// Correct code if "maxlength" (number of actual digits in string) = 14
char *addend1 = (char *) malloc(sizeof(char) * (maxlength + 1) ); // +1 To include termination byte
char *addend2 = (char *) malloc(sizeof(char) * (maxlength + 1) ); // +1 To include termination byte
char *sum = (char *) malloc(sizeof(char) * (maxlength + 2) ); // +2 To include termination byte, AND an extra char at the front
...so that the final actual digit character of sum is accessed via maxlength + 1
CORRECTED CODE
NOTE: Because calculating against maxlength as the number of digits (versus the length of the entire array including terminator) was confusing - as well as considered bad form, I have since learned - the following final code has been simplified to use more intuitive variables.
#include <stdio.h>
#include <stdlib.h>
char* addIntsAsStrings(char *s1, char *s2);
char* addIntsAsStrings(char *s1, char *s2){
// Find the length of the greater of the two
int length1 = strlen(s1);
int length2 = strlen(s2);
int addendDigits;
(length1 > length2) ? (addendDigits = length1) : (addendDigits = length2);
// We need separate strings of so they can be buffered with zeros
// Create the string for the addends and buffer with zeros.
char addend1[addendDigits + 1];
char addend2[addendDigits + 1];
for (int i = 0; i < (addendDigits) ; i++){ // "<" not "<="
addend1[i] = '0'; // buffer w/ 0s
addend2[i] = '0'; // buffer w/ 0s
} // buffer w/ 0s
addend1[addendDigits] = '\0'; // terminate
// put s1 and s2 into buffered addends strings
int s1_index = (strlen(s1) - 1);
int s2_index = (strlen(s2) - 1);
for (int i = (addendDigits - 1); i >= 0; i--){ //Start at back of addend
if ( s1_index >= 0) { addend1[i] = s1[s1_index]; }
if ( s2_index >= 0) { addend2[i] = s2[s2_index]; }
s1_index -= 1;
s2_index -= 1;
}
// Allocate sum pointer. The sum pointer needs to be ONE char
// longer than the addends, in the event that the addends need
// to carry a final one to the front. I.e. 999 + 999 = 1998
int sumDigits = addendDigits + 1;
char *sum = (char *) malloc(sizeof(char) * (sumDigits + 1) ); // +1 To include termination byte, AND an extra char at the front
for (int i = 0; i < (sumDigits) ; i++){ // "<" not "<="
sum[i] = '0'; // buffer w/ 0s
}
sum[sumDigits] = '\0';
// Manual addition vars
int a1, a2, total;
int carry = 0;
// Run the manual addition
// Start adding individual ints from end (right side)
for (int j = addendDigits - 1; j >= 0; j--) {
a1 = addend1[j] - '0'; // Convert to int
a2 = addend2[j] - '0'; // Convert to int
total = (a1 + a2 + carry);
carry = 0;
if ( total >= 10){
carry += 1;
total -= 10;
}
// convert to ascii value for numbers (adding 48)
sum[j + 1] = '0'+total; // sum[j + 1] because `sum`is always one index larger than the addends
}
sum[0] = '0' + carry; // add last carry to start of num always, even if 0
// Before returning, truncate leading zeros
char *returnsum = (char *) malloc(sizeof(char) * (strlen(sum) + 1) );
int sum_i = 0;
int returnsm_i = 0;
int truncate = 1; // true
while (1){
// if order is important here
if (sum[sum_i] == '\0') { break; } // we're done
if (sum[sum_i] == '0' && truncate == 1) { sum_i += 1; continue; } // 1 is true
// if a num, Stop truncating 0s but DO continue adding numbers
if (sum[sum_i] != '0') { truncate = 0; } // 0 is false
returnsum[returnsm_i] = sum[sum_i];
returnsm_i += 1;
sum_i += 1;
}
return returnsum;
}

How to concatenate char and string / How to make char to string

#include <stdio.h>
#include <string.h>
#include <stdbool.h>
#define MAX_SIZE 20
void main()
{
int i, j;
char *str1, *str2, *str3, *str_mid;
bool **lcs1, **lcs2;
int len1, len2, len3, len_mid;
char *ch = (char*)malloc(sizeof(char) * 3);
str1 = (char*)malloc(sizeof(char)*MAX_SIZE); //applicatian
str2 = (char*)malloc(sizeof(char)*MAX_SIZE); //apiasn
str3 = (char*)malloc(sizeof(char)*MAX_SIZE); //apun
str_mid = (char*)malloc(sizeof(char)*MAX_SIZE); //apn
str_mid = "";
scanf("%s", str1);
scanf("%s", str2);
scanf("%s", str3);
len1 = strlen(str1);
len2 = strlen(str2);
len3 = strlen(str3);
//str2, str3 ->str_mid (lcs1)
lcs1 = (bool**)malloc(sizeof(bool*)*(len3 + 1));
for (i = 0; i < len3 + 1; i++)
lcs1[i] = (bool*)malloc(sizeof(bool)*(len2 + 1));
for (i = 0; i < len3 + 1; i++)
for (j = 0; j < len2 + 1; j++)
lcs1[i][j] = false;
for (i = 1; i < len3 + 1; i++)
for (j = 1; j < len2 + 1; j++)
if (str3[i-1] == str2[j-1])
lcs1[i][j] = true;
for (i = 1; i < len3 + 1; i++)
{
for (j = 1; j < len2 + 1; j++)
if (lcs1[i][j])
{
//<--- error
ch = str3[i - 1];
strcat(str_mid, ch);
//--->
break;
}
}
//printf("%s", str_mid);
//str_mid, str1 (lcs2)
}
In <--- error ---> part,
I want to concatenation str3[i-1] and str_mid but, str3[i-1] is character type.
So make temporary string is ch and do concatenate.
But, the access error is occurred.
How to make char to string or, how to concatenate char and string?
So long as MAX_SIZE is large enough that str_mid will be able to hold the extra character, you can use strcat() and a compound literal (for C99 or later):
strcat(str_mid, (char[2]){ str3[i - 1], '\0' });
Note that in your code, str3[i - 1] is a char, while you have declared ch as a pointer to char.
How to concatenate char and string
There is no need to call any library function at all, nor to dynamically allocate any memory.
Assuming str_mid is large enough just do:
{
size_t tmp_len = strlen(str_mid);
str_mid[tmp_len] = str3[i - 1];
str_mid[tmp_len + 1] = '\0';
}
To still have the feeling to use a function you might want to wrap the above code into a macro like this:
#define STRCATCHAR(s, c) \
do { \
assert(s != NULL); \
size_t tmp_len = strlen(s); \
(s)[tmp_len] = (c); \
(s)[tmp_len + 1] = '\0'; \
} while (0)
and use it like this:
STRCATCHAR(str_mid, str3[i - 1]);
To concatenate char and string (first char, then string) do:
const char* conc_char_string(const char ch, const char* str, int len) {
const char* out = malloc(len + 2); /* 1 char from ch, len chars from str, and zero character, totally len+2 */
out[0] = ch; /* copy the character */
strcpy(out+1, str); /* copy the string */
return out;
}
Here len should be strlen(str)
The statement str_mid = ""; assigns the address of costant string "" to pointer str_mid, which will cause segmentfault later. Use str_mid[0] = '\0'; instead.
In statement ch = str3[i - 1];, you are assigning char to char pointer, which will cause segmentfault.
If all you want is simply appending a character to the string, you can keep track of length of string, like this:
len_mid = strlen(str_mid);
for (i = 1; i < len3 + 1; i++)
{
for (j = 1; j < len2 + 1; j++)
if (lcs1[i][j])
{
str_mid[len_mid++] = str3[i - 1];
str_mid[len_mid] = '\0';
break;
}
}
First rule of the C-club, create your own string buffer, second rule of the C-club create your own string buffer.
Each programmer has it's own string buffer. Minimal requirement is :
#define STRINGSTRTSZ 256
typedef struct string
{
char *str;
size_t n;
size_t buf_sz;
}String,*pString;
pString new_string()
{
pString res;
res=malloc(sizeof(String)); ///i leave to you the error handling
res->bus_sz=STRINGSTRSZ;
res->n=0;
res->str=malloc(res->bus_sz);
}
char*concatbase(pString *string,char*s,size_t len)
{
if(len+1>=string->buf_sz-string->n)
{
res->buf_sz+=len+res->buf_sz;
char *tmp=realloc(string->str,res->buf_sz);
if(tmp)
string->str=tmp;
else
...
}
memcpy(string->str+string->n,s,len);
string->n+=len;
*(string->str+string->n)='\0';
return string->str;
}
#define concatNllPtr(string,nllptr) concatbase(string,nllptr,strlen(nllptr))
#define concatString(string,str2) concatbase(string,(str2)->str,(str2)->n)
End so on...

Fastest way to reverse a string in C

Is this function the fastest, most optimized way of reversing a string in C? This runs in O(n/2) time. The optimization is that it only iterates through half of the string.
char* str_reverse(char *str, int len)
{
char word[len];
int i;
for (i = 0; i <= len / 2; ++i) {
word[i] = str[len - i - 1];
word[len - i - 1] = str[i];
}
word[len] = '\0';
return word;
}
Maybe something like this?
char *str_reverse_in_place(char *str, int len)
{
char *p1 = str;
char *p2 = str + len - 1;
while (p1 < p2) {
char tmp = *p1;
*p1++ = *p2;
*p2-- = tmp;
}
return str;
}
You'll find algorithms taking less instructions, like this in place reverse
char* str_reverse_in_place(char *str, int len)
{
int i;
for (i = len/2-1 ; i >= 0 ; --i) {
char c = str[i];
str[i] = str[len-i-1];
str[len-i-1] = c;
}
return str;
}
Optimizing for speed at that level, look at the inline keyword, also compile with (for gcc) with -O3 (does usually a better job that adding register ... by yourself).
If you need to have the reversed string elsewhere, either provide it in the function (being allocated for strlen(str)+1 - actually len+1 here - characters)
char* str_reverse(char *str, char *reverse, int len)
{
int i;
for (i = len-1 ; i >= 0 ; --i) {
reverse[i] = str[len-i-1];
}
reverse[len] = 0;
return reverse;
}
or malloc it (it will have to be freed by the caller).
char* str_reverse_malloc(char *str, int len)
{
char *reverse = malloc(len+1);
if ( ! reverse) return NULL;
int i;
for (i = len-1 ; i >= 0 ; --i) {
reverse[i] = str[len-i-1];
}
reverse[len] = 0;
return reverse;
}
The "most" optimized way must address the question of CPU and memory architecture as well as what is being reversed (long strings or short strings and what is the distribution).
There's no way to get relax the O(N) requirement, but one can use techniques such as loop unrolling, loop blocking and parallelism to optimize cache misses for very large strings. Also one can increase the word size and swap words, dwords or larger entities in-place (while dealing with the probable alignment issue).
// This will most likely be faster than byte-wise copying, but it's not O(N/8)...
if (len & 7 == 0)
{
uint32_t *dst = src+len-4;
uint32_t *src = (uint32_t *)ptr;
while (src<dst)
{
a = *src; b = *dst;
*src++ = byte_swap(b);
*dst-- = byte_swap(a);
}
}
int main() {
char str[100], temp;
int i, j = 0;
printf("\nEnter the string :");
fgets(str, sizeof(str), stdin);
i = 0;
j = strlen(str) - 1;
while (i < j) {
temp = str[i];
str[i] = str[j];
str[j] = temp;
i++;
j--;
}
printf("\nReverse string is :%s", str);
return (0);
}
Firstly, when considering complexity, it's impossible to do better than O(n) where n is the length of the string. Every single element - except for one element in the middle when n is odd - needs to be moved, and there is no way around it.
Also, this problem is so simple, so it's barely impossible to do something dramatic if you only consider the pure algorithm and not take real life factors into account. For very large strings, the single most important thing will be if the algorithm is cache friendly or not.
Here is a version which is cache friendly. It's not an in-place variant. If modified to in place, it can become even more cache friendly. First, we need a simple reverse function which does NOT terminate the destination string:
void str_reverse_aux(char *dest, const char *src, int len) {
for (int i = len-1 ; i >= 0 ; --i)
dest[i] = src[len-i-1];
}
After that, we use an algorithm that takes the N first and N last characters of a string, reverse both and swap their positions. Then move N step inwards from both directions and repeat until there is less than 2*N characters to process. Then we call the above function to finish things of.
void str_reverse(char *dest, const char *src, int block_size) {
int len = strlen(src);
char *d = dest;
const char *s = src;
int chunks = len / (2 * block_size);
char *dtail = &dest[len];
char *stail = &src [len];
for(int i=0; i<chunks; i++) { // Reverse the string blockwise
dtail -= block_size;
stail -= block_size;
char *buf = alloca(block_size); // Almost equivalent to char buf[block_size];
str_reverse_aux(buf, s, block_size);
memcpy(d, buf, block_size);
str_reverse_aux(buf, stail, block_size);
memcpy(dtail, buf, block_size);
d+=block_size;
s+=block_size;
}
str_reverse_aux(d, s, len - 2* chunks * block_size); // Take care of remainder
dest[len] = 0;
}
For very large strings, this will give a huge performance boost.
Here is a variation that does not require the length to be passed and will swap both the beginning and ending characters at a given offset within the string each pass through the loop:
/** strrevstr - reverse string, swaps src & dest each iteration.
* Takes valid string and reverses, original is not preserved.
* If str is valid, returns pointer to str, NULL otherwise.
*/
char *strrevstr (char *str)
{
if (!str) {
printf ("strrevstr() error: invalid string\n");
return NULL;
}
char *begin = str;
char *end = str + strlen (str) - 1;
char tmp;
while (end > begin)
{
tmp = *end;
*end-- = *begin;
*begin++ = tmp;
}
return str;
}
Here's one just for fun. But I reckon it's as fast as some of the others posted here.
The fun part is in the bitwise xor! Do that on paper, it works!
void inplace_swap(char *x, char *y) {
*y = *x ^ *y;
*x = *x ^ *y;
*y = *x ^ *y;
}
void reverse_string(char *str, int len) {
int first, last;
for (first = 0, last = len - 1; first < last; first++, last--) {
inplace_swap(&str[first], &str[last]);
}
}
This is faster than fastest way to reverse string in C ... ;)
#include <stdio.h>
#include <string.h>
int main(void){
char string[] = "hello";
int len = strlen(string);
char reverse[len];
for (int i = 0; i<len; i++){
reverse[i] = string[len - i - 1];
}
printf("%s",reverse);
return 0;
}

Resources