strncat off by one error - K&R C exercise 5-5 - c

My version of strncat is copying one too many chars into the destination and I cannot figure out why.
#include <stdio.h>
#define MAX_CHARS 20
void nconcatenate(char *start, char *end, int n)
{
if(sizeof start + n > MAX_CHARS)
return;
while(*start++);
start--; /* now points to the final char of start, the \0 */
int i;
for(i = 0; (*start++ = *end++) && i < n; i++);
*start = '\0';
}
int main()
{
char start[MAX_CHARS] = "str";
char *end = "ingy!";
nconcatenate(start, end, 3);
printf("start = %s\n", start);
return 0;
}
Using 3 as 'n' outputs
stringy
which is one too many chars.

Maybe because in the condition
(*start++ = *end++) && i < n
first it does (*start++ = *end++) and after that, it checks i < n.
I haven't tested it, but check it out and see.

Related

Every k-th digit cyclic problem using strings in C

Given some number in a form of string, I want to extract every k-th number from it. Then I go through the remaining string and extract every k-th number again. The thing I get as a result should be the number formed by these extracted ones(in a proper order). Example: 123456789, k = 3 --> 369485271
My algorithm is as follows: While the lenght of the string allows extracting every k-th number, I go through the string and store every k-th element in another string. Then I delete the extracted elements from the original string by tracking the proper index of an element and proceed forvard while the lenght of my str is sufficient.
I can't figure out what's the problem with my code. And maybe my approach isn't that good and there are some better/simpler ways of diong this?
#include <stdio.h>
#include <string.h>
void remove(char *str, unsigned int index) {
char *src;
for (src = str+index; *src != '\0'; *src = *(src+1),++src) ;
*src = '\0';
}
int main() {
char number[100];
char result[100];
int k;
printf("Enter a string: ");
scanf("%s",number);
printf("Enter a key: ");
scanf("%d",&k);
while (strlen(number)>k-1) {
for (int i = 0, p = 0; number[i] != '\0'; i++) {
if (i % k == (k-1)) {
result[p] = number[i];
p++;
}
}
for (int j = 0; number[j] != '\0'; j++){
if (j % k == (k-1)) {
remove(number, j);
j+=1; /*since the index was shifted due to removing an element*/
}
}
}
puts(result);
return 0;
}
You some issues:
You start writing your output from scratch again in each iteration of your while loop.
You do not handle the last digits
You do not treat the input as a cyclic input.
You do not terminate your output string.
remove is already a name of standard library function.
A shorter version could be this (untested):
#include <stdio.h>
#include <string.h>
void remove_digit(char *str, unsigned int index) {
char *src;
for (src = str+index; *src != '\0'; *src = *(src+1),++src)
;
}
int main() {
char number[100];
char result[100];
int k;
printf("Enter a string: ");
scanf("%s",number);
printf("Enter a key: ");
scanf("%d",&k);
int p = 0;
int i = 0;
int skip = k-1; // We remove 1 digit and skip k-1 digits
while (number[0] != 0) {
i = (i + skip) % strlen(number);
result[p] = number[i];
p++;
remove_digit(number, i);
}
number[p] = 0;
puts(result);
return 0;
}
The following code seems to be what you want:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
void remove_(char *str, unsigned int index) {
char *src;
for (src = str+index; *src != '\0'; *src = *(src+1),++src) ;
*src = '\0';
}
int main(int argc, const char * argv[]) {
char number[100];
char result[100];
int tmp[100];
int k;
printf("Enter a string: ");
scanf("%s",number);
printf("Enter a key: ");
scanf("%d",&k);
int p = 0;
for (int tp = 0; strlen(number) > k-1; tp = 0) {
for (int i = 0; number[i] != '\0'; i++)
if (i % k == (k-1))result[p++] = number[i];
for (int j = 0; number[j] != '\0'; j++)
if (j % k == (k-1)) tmp[tp++] = j;
for (; tp; --tp) remove_(number, tmp[tp-1]);
}
// The newly added code
for (int index; strlen(number); ) {
index = (k-1) % strlen(number);
result[p++] = number[index];
remove_(number, index);
}
puts(result);
return 0;
}
The most important thing is that every while loop, you need to remove the elements in number at once. While ensuring the integrity of your original code, I made some changes. Unfortunately, the main idea of ​​the original code is wrong.
It should circulate from the tail (including the rest) to the head after one round. But I found that the function of the code you provided is that after each round, the next round starts from the 0th element of the head.
By the way, your algorithm is similar to the Josephus problem

Is there a way if string repeats to return only repeated letters once?

I made code which will for string "aabbcc" return "abc" but in cases when there is more letters like "aaa" it will return "aa" instead of just one.
Here is the code I made.
void Ponavljanje(char *s, char *p) {
int i, j = 0, k = 0, br = 0, m = 0;
for (i = 0; i < strlen(s) - 1; i++) {
for (j = i + 1; j < strlen(s); j++) {
if (s[i] == s[j]) {
br++;
if (br == 1) {
p[k++] = s[i];
}
}
}
br = 0;
}
p[k] = '\0';
puts(p);
}
For "112233" output should be "123" or for "11122333" it should be also "123".
Avoid repeated calls to strlen(s). A weak compiler may not see that s is unchanged and call strlen(s) many times, each call insuring a cost of n operations - quite inefficient. #arkku.1 Instead simply stop iterating when the null character detected.
Initialize a boolean list of flags for all char to false. When a character occurs, set the flag to prevent subsequent usage. Be careful when indexing that list as char can be negative.
Using a const char *s allows for wider allocation and helps a compiler optimization.
Example:
#include <stdbool.h>
#include <limits.h>
void Ponavljanje(const char *s, char *p) {
const char *p_original = p;
bool occurred[CHAR_MAX - CHAR_MIN + 1] = { 0 }; // all values set to 0 (false)
while (*s) {
if (!occurred[*s - CHAR_MIN]) {
occurred[*s - CHAR_MIN] = true;
*p++ = *s;
}
s++;
}
*p = '\0';
puts(p_original);
}
1 #wrongway4you comments that many compilers may assume the string did not change and optimize out the repeated strlen() call. A compliant compiler cannot do that though without restrict unless it is known that in all calls, s and p do not overlap. A compiler otherwise needs to assume p may affect s and warrant a repeated strlen() call.
does the work with a complexity O(n)
I suppose programming can give rmg
void Ponavljanje(char *s,char *p)
{
char n[256] = {0};
int i = 0;
while (*s) {
switch (n[(unsigned char) *s]) {
case 0:
n[(unsigned char) *s] = 1;
break;
case 1:
p[i++] = *s;
n[(unsigned char) *s] = 2;
}
s += 1;
}
p[i] = 0;
puts(p);
}
While the inner loop checks br to only copy the output on the first repetition, the outer loop still passes over each repetition in s on future iterations. Hence each further occurrence of the same character will run a separate inner loop after br has already been reset.
With aaa as the input, both the first and the second a cause the inner loop to find a repetition, giving you aa. In fact, you always get one occurrence fewer of each character in the output than there is in the input, which means it only works for 1 or 2 occurrences in the input (resulting in 0 and 1 occurrences, respectively, in the output).
If you only want to remove the successive double letters, then this function would be sufficient, and the examples given in the question would fit:
#include <stdio.h>
void Ponavljanje(char *s,char *p)
{
char dd = '\0';
char *r;
if(s == NULL || p == NULL)
return;
r = p;
while(*s){
if(*s != dd){
*r = *s;
dd = *s;
r++;
}
s++;
}
*r = '\0';
puts(p);
}
int main(void)
{
char s[20] = "1111332222";
char p[20];
Ponavljanje(s,p);
}
Here is something that works regardless of order:
#include <stdio.h>
#include <string.h>
void
repeat(char *s, char *p)
{
int slen;
int sidx;
int pidx;
int plen;
int schr;
slen = strlen(s);
plen = 0;
for (sidx = 0; sidx < slen; ++sidx) {
schr = s[sidx];
// look for duplicate char
int dupflg = 0;
for (pidx = 0; pidx < plen; ++pidx) {
if (p[pidx] == schr) {
dupflg = 1;
break;
}
}
// skip duplicate chars
if (dupflg)
continue;
p[plen++] = schr;
}
p[plen] = 0;
puts(p);
}
int
main(void)
{
char p[100];
repeat("112233",p);
repeat("123123",p);
return 0;
}
Note: As others have mentioned, strlen should not be placed in the loop condition clause of the for [because the length of s is invariant]. Save strlen(s) to a separate variable and loop to that limit
Here is a different/faster version that uses a histogram so that only a single loop is required:
#include <stdio.h>
#include <string.h>
void
repeat(char *s, char *p)
{
char dups[256] = { 0 };
int slen;
int sidx;
int pidx;
int plen;
int schr;
slen = strlen(s);
sidx = 0;
plen = 0;
for (sidx = 0; sidx < slen; ++sidx) {
schr = s[sidx] & 0xFF;
// look for duplicate char
if (dups[schr])
continue;
dups[schr] = 1;
p[plen++] = schr;
}
p[plen] = 0;
puts(p);
}
int
main(void)
{
char p[100];
repeat("112233",p);
repeat("123123",p);
return 0;
}
UPDATE #2:
I would suggest iterating until the terminating NUL byte
Okay, here's a full pointer version that is as fast as I know how to make it:
#include <stdio.h>
#include <string.h>
void
repeat(char *s, char *p)
{
char dups[256] = { 0 };
char *pp;
int schr;
pp = p;
for (schr = *s++; schr != 0; schr = *s++) {
schr &= 0xFF;
// look for duplicate char
if (dups[schr])
continue;
dups[schr] = 1;
*pp++ = schr;
}
*pp = 0;
puts(p);
}
int
main(void)
{
char p[100];
repeat("112233",p);
repeat("123123",p);
return 0;
}

Counting how many times the char sir2 appears in sequence sir1[]

I've got a problem with some chars and i can't figure out how to solve it. It is given a sequence of strings and another string. And I have to count the number of appearances of the string in the sequence of strings. I did the program below but it doesn't work.
main.cpp
#include "tipulbool.h"
char sir1[25], sir2;
int n, i, k;
int main (){
cin>>n;
for(i = 1; i <= n; i++)
cin>>sir1[i];
cin>>sir2;
for(i = 1; i <= n; i++)
k += secventa(sir1[i], sir2);
cout<<k;
return 0;
}
tipulbool.h
#include <iostream>
#include <string.h>
using namespace std;
int secventa (char sir1[], char sir2);
tipulbool.cpp
#include "tipulbool.h"
int secventa (char sir1[], char sir2){
int contor;
char *p;
p = strstr(sir1[], sir2);
if(p)
contor++;
while(p){
p = strstr(p + 1, sir2);
if(p)
contor++;
}
return contor;
}
try this:
int countOfChars(char sir1[], char sir2)
{
int count = 0;
char* p = sir1;
while (*p)
{
if (p == sir2)
{
++count;
}
++p;
}
return count;
}
this code:
#include "tipulbool.h"
int secventa (char sir1[], char sir2){
int contor;
char *p;
p = strstr(sir1[], sir2);
if(p)
contor++;
while(p){
p = strstr(p + 1, sir2);
if(p)
contor++;
}
return contor;
}
gets lost in it self.
suggest:
#include "tipulbool.h"
int secventa (char sir1[], char sir2){
int contor = 0;
char *p = str1;
while(NULL != (p = strchr(p, str2) ) )
{
contor++;
p++;
}
return contor;
}
However, note that
1) I used `strchr()` rather than `strstr()`
because `str2` is a single char, not a string
2) I removed the repetitive code
3) `str1` MUST be a NULL terminated string, which in the posted code is not the case.
regarding this code:
#include "tipulbool.h"
char sir1[25], sir2;
int n, i, k;
int main (){
cin>>n;
for(i = 1; i <= n; i++)
cin>>sir1[i];
cin>>sir2;
for(i = 1; i <= n; i++)
k += secventa(sir1[i], sir2);
cout<<k;
return 0;
}
in C++, an array offset starts with 0 and continues to (length of array -1)
variables that are only used in a single function should (in general) be defined as local/auto variables within that function.
suggest using code similar to the following:
#include "tipulbool.h"
int main ( void )
{
char sir2;
int n; // will contain number of char in str1
int i; // loop counter
int k = 0; // will contain number of occurrence of str2 in str1
// get count of chars in first string
cin >> n;
// allocate room for first string (using C string)
// +1 to allow for NUL terminator byte
char *sir1 = new char[n+1];
// initialize first string
memset( sir1, 0x00, n+1 );
// input first string
for(i = 0; i < n; i++)
cin >> sir1[i];
// input target char
cin >> sir2;
// get count of occurances of str2 in str1
k = secventa(sir1, sir2);
cout << k << endl;
delete [] str1;
return 0;
}
Since this is C++, you might want to look at vector or string
to simplify the written part of the code even further

Writing to char array, empty output

I'm wondering what's occurring here, I allocate space for the dest variable outside of the reverse function. And then write to it in the function call. But my output is blank. I want to do this without malloc, purely for educational purposes. Thanks
#include <stdio.h>
#include <string.h>
/*function declaration*/
void reverse(char *src, char *dest);
int main(void) {
char src[] = "hello, world";
char dest[strlen(src)];
reverse(src, dest);
printf("%s\n", dest);
return 0;
}
/*function reverse a string*/
void reverse(char *src, char *dest) {
int i;
int j = 0;
for(i = strlen(src); i >= 0; i--) {
*(dest+j) = *(src+i);
++j;
}
}
1) Since your iteration starts with
for(i = strlen(src); i >= 0; i--) {
it assigns 0 to the dest, thus terminating the string. So printf() prints nothing as it sees the 0 terminator. You can re-write it as:
for(i = strlen(src) - 1; i >= 0; i--) {
*(dest+j) = *(src+i);
++j;
}
dest[j] = 0; /* terminates the string */
2) You have another problem. Your dest doesn't have enough space. It should be:
char dest[strlen(src) + 1]; /* Notice the +1 */
3) You should use size_t for array indexes instead of int.
You are forgetting about the null character in your strings. dest is not large enough to hold the reverse of the string since there needs to be a null-terminator:
char dest[ strlen(src) + 1 ];
Then in the loop you are copying the null-terminator to the front of the string so it is essentially the string "" but using more memory. You need to start at the index strlen() - 1:
for(i = strlen(src)-1; i >= 0; i-- ){
The problem is with the for(i=strlen(src)..... in reverse(). src[strlen(src)] is 0 (zero; string-termination). So when you get to printf(), it sees the firs char and outputs an empty string.
Use strlen(str)-1, and then *(dest+j) = 0 after the for-loop.
In total, reverse() should be:
void reverse(char *src, char *dest) {
int i;
int j = 0;
for(i = strlen(src)-1; i >= 0; i--) {
*(dest+j) = *(src+i);
++j;
}
*(dest+j) = 0;
}

Reversing a string in C does not output the reversed line

I'm trying to reverse a string in C. The reverse function simply assigns the character at a given location (in a for loop) to a temp object. I cannot see any logic errors within the program, and the program compile successfully under gcc 4.7.2 with this command:
gcc -Wall -std=c99 reverse.c
To recreate the problem:
1.) Run the program and enter a string into your shell
2.) Once finished inputting, press enter/and or your EOF signal.
The problem is that neither the original string is printed, or the reversed string. This is also an exercise from K&R second edition, if you have completed this exercise, a different solution to mine would be appreciated.
I think the bug is caused by the absence of a null character, the famous printf requires a null terminated string to print input to cin. The getline function assigns a null character to the end of the array, surely the null character will be the first character in the string thereto ending the printf (and thus no character/literal is printed).
#include <stdio.h>
#define MAXLINE 1000
int geline(char s[], int lim);
void reverse(char line[], int length);
int main()
{
char s[MAXLINE];
char t[MAXLINE];
int k, len;
while ((len = getline(s, MAXLINE)) > 0) {
if (len > 1)
reverse(s, len);
}
printf("%s", s);
return 0;
}
void reverse (char input[], int length)
{
char temp[MAXLINE];
int j = length;
for (int i = 0; i < length; ++i, --j) {
temp[i] = input[i];
input[i] = input[j];
input[j] = temp;
}
}
int getline(char s[], int lim)
{
int c, i;
for (i=0; (c=getchar()) != EOF && c!='\n'; ++i)
s[i] = c;
if (c== '\n') {
s[i] = c;
++i;
}
s[i] = '\0';
return i;
}
There are two logic errors:
int j = length; should be int j = length - 1;
temp[i] = input[i] ... input[j] = temp;
There are two approaches for that last error:
Define temp as a single char: char temp; ... temp = input[i]; input[i] = input[j]; input[j] = temp;
Use the correct index in temp: temp[i] = input[i]; input[i] = input[j]; input[j] = temp[i]
Try this code:
#include <stdio.h>
#define MAXLINE 1000
int geline(char s[], int lim);
void reverse(char line[], int length);
int main () {
char s[MAXLINE];
char t[MAXLINE];
int k, len;
while ((len = getline(s, MAXLINE)) > 0) {
if (len > 1)
reverse(s, len);
}
printf("%s", s);
return 0;
}
void reverse (char input[], int length) {
char temp;
int j = length - 1;
for (int i = 0; i < j; ++i, --j) {
temp = input[i];
input[i] = input[j];
input[j] = temp;
}
}
int getline (char s[], int lim) {
int c, i;
for (i=0; (c=getchar()) != EOF && c!='\n'; ++i)
s[i] = c;
if (c== '\n') {
s[i] = c;
++i;
}
s[i] = '\0';
return i;
}
(I did my compiling with -Wall -std=c99 -O3 -g, the -g to allow use of gdb)
Here are the things I noticed and some ways of addressing them. I've tried to hew pretty closely to the style you started with (I would have converted the array decls in the prototypes to pointers, for example, but that's not necessary).
Your getline prototype was missing the t.
int getline(char s[], int lim);
In main, you don't actually need k, t[MAXLINE], and your printf should probably be in the loop so you'll see each word as it's reversed. Note that printf picks up a \n, since the getline below converts both newline and EOF-terminated lines to the same thing (without newlines):
int main()
{
char s[MAXLINE];
int len;
while ((len = getline(s, MAXLINE)) > 0) {
if (len > 0)
reverse(s, len);
printf("%s\n", s);
}
return 0;
}
In above, the getline(s, MAXLINE) could have been getline(s, sizeof(s) / sizeof(*s) - 1) although again, be careful of fencepost errors (note the - 1).
The reverse function can be greatly improved without going over to the madness of xor to skip having a variable (although Daffra's example is interesting, especially in that it correctly stops in the middle). Instead, having the sense to just index up to the halfway point is a clear win. Between that and dropping reducing the temp array to just a temporary character, your general style is retained.
void reverse (char input[], int length)
{
int max = length - 1; /* keep the final NUL in place */
for (int i = 0; i <= max / 2; ++i) {
char ch = input[i];
input[i] = input[max - i];
input[max - i] = ch;
}
}
In the above gcc -O3 can do a serious workover on the code, so there's no real reason to worry that long division is going to be performed on every loop test, etc. For example, gdb reports that i itself gets optimized out automatically, which is pretty interesting. Write good, readable code first, have some faith in your compiler, optimize later.
And last, getline benefits from testing against lim (CRITICAL!) and and converting newlines into NULs.
int getline(char s[], int lim)
{
int i, c;
for (i=0; (i <= lim) && ((c=getchar()) != EOF) && (c != '\n'); ++i)
s[i] = c;
s[i] = '\0';
return i; /* return the index to the final NUL, same as length w/o it */
}
Setting MAXLINE to 10 temporarily shows that this version handles overlong lines fairly gracefully, splitting them into two separate ones without losing any of the characters.
Be careful with strings to very clearly decide whether you want to describe them in terms of length, or in terms of the index to the NUL at the end. This affects how you phrase your loops, limits, variable names, etc, and obviously confusing them is a classic source of fencepost errors.
Hope this helps.
int j = length - 1; // Thanks to #chux
for (int i = 0; i < j; ++i, --j) { // or <= length / 2
char temp = input[i];
input[i] = input[j];
input[j] = temp;
temp is not needed, and not entirely correctly used.
You are twice swapping the values, which restores the swap on the second half of the cycling. :)
Your prototype misses a 't' (geline). Hence maybe
ssize_t getline(char **lineptr, size_t *n, FILE *stream);
is taken?
you can use this fast function :
inline char * reverse(char *p)
{
char *save=p;
char *q = p;
while(q && *q) ++q;
for(--q; p < q; ++p, --q)
*p = *p ^ *q,
*q = *p ^ *q,
*p = *p ^ *q;
return save ;
}
Please have a look at this code:
#include <stdio.h>
#define MAXLINE 1000
int geline(char s[], int lim);
void reverse(char line[], int length);
int main()
{
char s[MAXLINE];
int len;
while ((len = geline(s, MAXLINE)) > 1) {
if (len > 1) {
reverse(s, len);
printf("%s", s);
}
}
return 0;
}
void reverse (char input[], int length)
{
char temp;
int j = length-1;
for (int i = 0; i < j; ++i, --j) {
temp = input[i];
input[i] = input[j];
input[j] = temp;
}
}
int geline(char s[], int lim)
{
int c, i;
for (i=0; (c=getchar()) != EOF && c!='\n'; ++i)
s[i] = c;
if (c== '\n') {
s[i] = c;
++i;
}
s[i] = '\0';
return i;
}
Only 2 changes needed here, and it will do the reverse fine.
Inside function reverse just do this
int j = --length;
Instead of this:
input[j] = temp; //you should use
input[j] = temp[i];

Resources